I cant make Linq compare geodistances? - linq

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);
}
}
}

Related

Having trouble trying to order using linq

I am trying to order by start date(s.StartDate). Below is my code so far, my understanding is that I should be adding .orderby(s.StartDate) somewhere but I don't think I'm even taking the correct route now as I have tried many ways.
var query = from s in context.SessionSearch
where s.Children == 0 && s.IsPublic == isPublic
select s;
var query = from s in context.SessionSearch
where s.Children == 0 && s.IsPublic == isPublic
if (startDate != null)
{
query = query.Where(s => s.StartDate >= startDate && s.StartDate <= endDate);
}
You should be able to start with the "without startdate" option - you have a couple of options here - either declare the type of the query specifically:
IQueryable<SessionSearch> query = from s in context.SessionSearch
where s.Children == 0 && s.IsPublic == isPublic
order by s.StartDate
select s;
And then as you've tried, add the additional where clause to the query if there's a start date passed in:
query = query.Where(s => s.StartDate >= startDate && s.StartDate <= endDate);
This means that you would not be able to add further ordering via the ThenBy methods.
Alternatively, you can add the order by clause after you've finished adding the where clauses:
var query = from s in context.SessionSearch
where s.Children == 0 && s.IsPublic == isPublic
select s;
if (startDate != null) {
query = query.Where(s => s.StartDate >= startDate && s.StartDate <= endDate);
}
query = query.OrderBy(s => s.StartDate);
Props to JonSkeet for pointing these out.

Terrible performance using transformblocks

I currently am trying to use TransformBlocks to make my code run faster. Instead, I find that I have achieved essentially no parallelization:
As you can see, there is quite a bit of dead space, with very little I/O or other issues preventing things from running in parallel (note: all the green blocks are the main thread).
The basic structure of the calling code is as follows:
var options = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 8 };
var download = new TransformBlock<string, Tuple<string, string>>(s => sendAndReciveRequest(s), options);
var process = new TransformBlock<Tuple<string, string, TransformBlock<string, Tuple<string, string>>>, List<string>>(s => Helpers.ParseKBDL(s), options);
var toObjects = new TransformBlock<List<string>, List<Food>>(list => toFood(list), options);
for (char char1 = 'a'; char1 < 'z' + 1; char1++)
download.Post(char1.ToString());
while ((download.InputCount != 0 || download.OutputCount != 0 || process.InputCount != 0) || (Form1.downloadCount != Form1.processCount))
{
if (download.OutputCount == 0 && download.InputCount == 0)
{
continue;
}
var res = download.Receive();
process.Post(new Tuple<string, string, TransformBlock<string, Tuple<string, string>>>(res.Item1, res.Item2, download));
}
Note: The reason for the messy checks and tuples is because occasionally process needs to add more to the download block. I am very open to changing how this is organized.
So my question: Why is this code achieving no speedup? How can I restructure it so that I do get a speedup?

query cannot be enumerated more than once

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()));

search fails with maxid (ulong.max) and sinceid set, but separately it works

the following query works ok if you comment out either the SinceID or the MaxID clause, but when both are included a "bad url" exception is generated.
var maxId = ulong.MaxValue;
var sinceId = (ulong)341350918903701507;
var searchResult =
(
from search in ctx.Search
where search.Type == SearchType.Search &&
search.ResultType == ResultType.Mixed &&
search.Query == "red wedding" &&
search.SinceID == sinceId &&
search.MaxID == maxId &&
search.IncludeEntities == false &&
search.Count == 200
select search).SingleOrDefault();
If you look at the query result in Fiddler, you'll see that the response is:
{"errors":[{"code":195,"message":"Missing or invalid url parameter"}]}
I can't respond to why Twitter wouldn't accept the query with both SinceID and MaxID. However, the query is formed correctly and there isn't any documentation describing constraints on the relationship between these two parameters for this particular scenario. The purpose of the MaxID is to be the id of the highest tweet to return on the next query. Both MaxID and SinceID are intended to help you page through data. I wrote a blog post on how to do this:
Working with Timelines with LINQ to Twitter
I seem to have the same problem as you are, so the only solution I have was to do it manually, so first I retrieved the the first list setting the sinceId value to the one I have like this:
var searchResult =
(
from search in TwitterCtx.Search
where search.Type == SearchType.Search &&
search.Query == query &&
search.Count == pageSize &&
search.IncludeEntities == true &&
search.ResultType == ResultType.Recent &&
search.SinceID == sinceId
select search
).SingleOrDefault<Search>();
resultList = searchResult.Statuses;
Then I have to search for other tweets (the case when new tweets count is more the pageSize) so I had a while loop like this:
ulong minId = ulong.Parse(resultList.Last<Status>().StatusID) - 1;
List<Status> newList = new List<Status>();
while (minId > sinceId)
{
resultList.AddRange(newList);
searchResult =
(
from search in TwitterCtx.Search
where search.Type == SearchType.Search &&
search.Query == query &&
search.Count == pageSize &&
search.IncludeEntities == true &&
search.ResultType == ResultType.Recent &&
search.MaxID == minId &&
search.SinceID == sinceId
select search
).SingleOrDefault<Search>();
newList = searchResult.Statuses;
if (newList.Count == 0)
break;
minId = ulong.Parse(newList.Last<Status>().StatusID) - 1;
}
Now for some reason here you can use both sinceId and maxId.
Just in case anyone else comes across this, I encountered this issue when the MaxId was an invalid Tweet Id.
I started off using zero but ulong.MaxValue has the same issue. Switch it out with a valid value and it works fine. If you're not using SinceId as well it seems to work fine.
I used to get same error "Missing or invalid url parameter", but as per Joe Mayo's solution, I have additionally added if(sinceID < maxID) condition before do while loop, because the query throws above error whenever maxID is less than sinceID, I think which is incorrect.
if (sinceID < maxID)
{
do
{
// now add sinceID and maxID
searchResponse =
await
(from search in twitterCtx.Search
where search.Type == SearchType.Search &&
search.Query == "from:#" + twitterAccountToDisplay + " -retweets" &&
search.Count == count &&
search.SinceID == sinceID &&
search.MaxID == maxID
select search)
.SingleOrDefaultAsync();
if (searchResponse == null)
break;
if (searchResponse.Count > 0 && searchResponse.Statuses.Count > 0)
{
newStatuses = searchResponse.Statuses;
// first tweet processed on current query
maxID = newStatuses.Min(status => status.StatusID) - 1;
statusList.AddRange(newStatuses);
lastStatusCount = newStatuses.Count;
}
if (searchResponse.Count > 0 && searchResponse.Statuses.Count == 0)
{
lastStatusCount = 0;
}
}
while (lastStatusCount != 0 && statusList.Count < maxStatuses);
//(searchResponse.Count != 0 && statusList.Count < 30);
}

text box percentage validation in javascript

How can we do validation for percentage numbers in textbox .
I need to validate these type of data
Ex: 12-3, 33.44a, 44. , a3.56, 123
thanks in advance
sri
''''Add textbox'''''
<asp:TextBox ID="PERCENTAGE" runat="server"
onkeypress="return ispercentage(this, event, true, false);"
MaxLength="18" size="17"></asp:TextBox>
'''''Copy below function as it is and paste in tag..'''''''
<script type="text/javascript">
function ispercentage(obj, e, allowDecimal, allowNegative)
{
var key;
var isCtrl = false;
var keychar;
var reg;
if (window.event)
{
key = e.keyCode;
isCtrl = window.event.ctrlKey
}
else if (e.which)
{
key = e.which;
isCtrl = e.ctrlKey;
}
if (isNaN(key)) return true;
keychar = String.fromCharCode(key);
// check for backspace or delete, or if Ctrl was pressed
if (key == 8 || isCtrl)
{
return true;
}
ctemp = obj.value;
var index = ctemp.indexOf(".");
var length = ctemp.length;
ctemp = ctemp.substring(index, length);
if (index < 0 && length > 1 && keychar != '.' && keychar != '0')
{
obj.focus();
return false;
}
if (ctemp.length > 2)
{
obj.focus();
return false;
}
if (keychar == '0' && length >= 2 && keychar != '.' && ctemp != '10') {
obj.focus();
return false;
}
reg = /\d/;
var isFirstN = allowNegative ? keychar == '-' && obj.value.indexOf('-') == -1 : false;
var isFirstD = allowDecimal ? keychar == '.' && obj.value.indexOf('.') == -1 : false;
return isFirstN || isFirstD || reg.test(keychar);
}
</script>
You can further optimize this expression. Currently its working for all given patterns.
^\d*[aA]?[\-.]?\d*[aA]?[\-.]?\d*$
If you're talking about checking that a given text is a valid percentage, you can do one of a few things.
validate it with a regex like ^[0-9]+\.?[0-9]*$ then just convert that to a floating point value and check it's between 0 and 100 (that particular regex requires a zero before the decimal for values less than one but you can adapt it to handle otherwise).
convert it to a float using a method that raises an exception on invalid data (rather than just stopping at the first bad character.
use a convoluted regex which checks for valid entries without having to convert to a float.
just run through the text character by character counting numerics (a), decimal points (b) and non-numerics (c). Provided a is at least one, b is at most one, and c is zero, then convert to a float.
I have no idea whether your environment support any of those options since you haven't actually specified what it is :-)
However, my preference is to go for option 1, 2, 4 and 3 (in that order) since I'm not a big fan of convoluted regexes. I tend to think that they do more harm than good when thet become to complex to understand in less than three seconds.
Finally i tried a simple validation and works good :-(
function validate(){
var str = document.getElementById('percentage').value;
if(isNaN(str))
{
//alert("value out of range or too much decimal");
}
else if(str > 100)
{
//alert("value exceeded");
}
else if(str < 0){
//alert("value not valid");
}
}

Resources