Math functions within Linq to Entities - vb.net

I have some older methods using sql strings and connections that I'm trying to convert to Linq to Entities.
I have rewritten the sql to an ef query as follows;
Using ctx As New DataEntities()
Dim station As String = (From sta In ctx.weather_stations
Let distance = SqlFunctions.SquareRoot(Math.Pow(69.1 * (sta.latitude - lat), 2) + Math.Pow(69.1 * (longi - sta.longitude) * SqlFunctions.Cos(sta.latitude / 57.3), 2))
Where distance < withinRange
Order By distance
Select sta.station_id).Take(1).ToString()
If Not String.IsNullOrEmpty(station) Then
Return station
Else
Return String.Empty
End If
End UsingData
This gives an error,
LINQ to Entities does not recognize the method 'Double Sqrt(Double)' method, and this method cannot be translated into a store expression.
Can this query be done in Linq to EF? If so, how can I reconstruct this query to work?

Related

chain string expression linq

In traditional sql we can chain expression according to if statements.
for example lets say I have variable called "firstName" and I want to get from database all users according to the value in this variable(if empty get all users)
so I will chain the sql string like that
string sql="";
if(firstname!="")
sql=String.format(" And firstname='{0}',firstName)
.ExecuteReader(System.Data.CommandType.Text,"select * from users where 1=1" + sql)
Is there a way to copy this Technique to linq expression?
something like
from U in user
where 1=1 & sql
select U
Change to method syntax instead of query syntax, and chaining is easy.
var query = user.Select(u => u);
if(firstname!="")
query = query.Where(u => u.firstname = firstname);
queries in query syntax are converted at compile-time, so there's not a mechanism to "inject" sql at run time using query syntax.

Using contains in Linq query produces error

I have a pretty straight forward query that is producing this error at runtime: Only arguments that can be evaluated on the client are supported for the String.Contains method.
The query is supposed to find only the categories that have transfers assigned to them. The transfers can be listed in several categories so there is no table relationship. The Categoryidhash contains data like "7~34~25~42~47". I just realized while writing this that searching for '7' will return multiple results, "7" & '47' Etc. Thats ok i'll just change id's to all double digits. meanwhile...
How can I fix this?
Private Function GetCategoryList() As List(Of Category)
Dim lst As List(Of Category) = New List(Of Category)
Using db As New IPCDataDataContext
lst = (From c In db.Categories
From t In db.Transfers
Where t.CategoryIDhash.Contains(c.ID.ToString)
Select c).ToList()
Return lst
End Using
End Function
The exception means that Contains only accepts arguments that can be converted to fixed variable in SQL. So something like Where t.CategoryIDhash.Contains(someVariable.ToString) would be possible, because someVariable.ToString can be evaluated client-side.
I don't really understand this restriction, because in SQL it is perfectly possible to use a LIKE clause with a string that is built in the SQL statement itself. This is demonstrated by the statement that fixes your problem:
lst = (From c In db.Categories
From t In db.Transfers
Where SqlMethods.Like(t.CategoryIDhash, "%" + c.ID.ToString "%")
Select c).ToList()
This generates (and executes) SQL like
...
WHERE [t1].[CategoryIDhash] LIKE (#p0 + (CONVERT(NVarChar,[t0].[ID]))) + #p1
(where #p0 and #p1 are the % characters.
Although you could do this, I wonder if you're on the right track by using this CategoryIDhash. I think you should convert it to a FK relationship (if it's in your hands to modify the database).

How can I produce a join on a substring and an integer in Entity Framework?

Using Entity Framework, I'm trying to join two tables like this.
...
join f in ent.FTypes on Int32.Parse(c.CourseID[0].ToString()) equals f.FTypeID
...
The first character of the string CourseID is a digit, and FTypeID is an int.
This doesn't seem to work though. The exception message I get is:
LINQ to Entities does not recognize the method 'Int32 Parse(System.String)' method, and this method cannot be translated into a store expression."} System.Exception {System.NotSupportedException}
What I want to replicate is the SQL string equivalent (which works fine):
join FType f on SUBSTRING(c.CourseID, 1, 1) = f.FTypeID
Does anyone have the solution to have to do this in LINQ to Entities?
This is a rather nasty join, but I did some testing in Entity Framework with similar data and arrived at something you can test on yours.
It uses string.Substring to grab the first character from your string operand, and then uses a combination of the EF-only method SqlFunctions.StringConvert (these methods are found in System.Data.Objects.SqlClient) with a cast to double1 and finally a string.Trim2 for your integer operand.
I have tested this and confirmed that all functions are supported at least in EF 4. Several other methods proposed or featured in the question do not work, because Entity Framework does not know how to tranlsate them to the appropriate SQL equivalent.
join f in ent.FTypes
on c.CourseID.Substring(0, 1)
equals SqlFunctions.StringConvert((double)f.FTypeID).Trim()
It produces a SQL join that looks like the following:
INNER JOIN [dbo].[FTypes] AS [Extent2]
ON ((SUBSTRING([Extent1].[CourseID], 0 + 1, 1)) = (LTRIM(RTRIM(STR( CAST( [Extent2].[FTypeID] AS float))))))
OR ((SUBSTRING([Extent1].[CourseID], 0 + 1, 1) IS NULL) AND (LTRIM(RTRIM(STR( CAST( [Extent2].[FTypeID] AS float)))) IS NULL))
So based on that, you might want to do some additional filtering as necessary.
Give it a shot and see if that helps solve the problem.
1 The cast to double is necessary because SqlFunctions.StringConvert does not have an overload for integer and there is no other single best match, so I force one.
2 The resultant string needs to be trimmed because the string conversion generates some excess padding.
I'm not sure this worked 8 years ago, but modern EF6 implementation allows you to add anonymous types, so you can add the conversion to your initial select statements, and then you can just compare that new properties directly in the join.
from c in ent.Courses
select new { typeId = c.CourseId.Substring(0, 1), c }
join f in ent.FTypes.Select(t => new { stringId =
t.FTypeId.ToString(), t }
on c.typeId equals f.stringId into ...
NOTE: The join-statement does not appear to support ToString() but the select-statements do.
You could also move the CouresId.Substring into the join statement, but that may run less efficiently there.
Results in SQL like this:
INNER JOIN [ent].[FTypes] AS [Extent2] ON
(LTRIM(RTRIM(SUBSTRING([Extent1].[nvarchar(max)], 3 + 1, 1)))) =
CAST([Extent2].[Id] AS nvarchar(max))

boolean Contains does not support conversion to SQL

I got error boolean Contains does not support conversion to SQL
I have array of the rooms id which should be removed
I want to remove with this query but I can't succeed. How may I do this?
var rooms = from rooms in entity.Rooms
where myArray.contains(rooms.RoomID) select rooms;
You are trying to mix the call. Array.Contains happens on client, but you are putting it into the middle of the expression that is sent to the sql server.
You need to either send the whole lot to sql for the comparison, or return the rows from sql and compare them locally.
rooms = from room in entity.Rooms.AsEnumerable()
where myArray.contains(room.RoomID)
select room;
or using lambda syntax:
rooms = entity.Rooms.AsEnumerable.Where(room => myArray.contains(room.RoomID));
var roomslist =myArray.ToList();
var roomsTemp = entity.Rooms;
var rooms=roomsTemp.Where(x=>roomslist.Contains(x.RoomId)).ToList();
If that does not work then this should work but i dont recomend using this:
var roomslist =myArray.ToList();
var roomsTemp = entity.Rooms.ToList();
var rooms=roomsTemp.Where(x=>roomslist.Contains(x.RoomId)).ToList();
It's been a while since L2S, but here goes. I could of sworn int[].Contains was built in.. are you using GUIDs by chance? Maybe I'm thinking of EF. Does this help: Linq query with Array in where clause?
If you were to write a SQL Procedure for that, you would pass in a CSV string of the IDs. Using the same idea you could (warning off the top of my head):
var ids = "," + string.Join(",",myArray) + ",";
var rooms = from rooms in entity.Rooms
where SqlMethods.Like("," + rooms.RoomID + ",", ids) select rooms;
Maybe ids.Contains("," + rooms.RoomID + ",") would work as that has a translation to SQL Built in (for strings)
Alternate is to write your own UDF that breaks the CSV into a table and does a SELECT EXISTS. There should be plenty of examples of this as it's the way you do it with Stored Procedures. Then you could:
var ids = "," + string.Join(",",myArray) + ",";
var rooms = from rooms in entity.Rooms
where MyMethods.Contains(ids,rooms.RoomID) select rooms;

Surrounding Areas SQL

I'm trying to get a query to work which returns a list of locId's from the database when fed a long and a lat.
Here's the sql:
eg: lat = "-37.8333300" : lon = "145.000000" : radius = (5 * 0.621371192) ^ 2
SELECT locId,longitude,latitude FROM tbliplocations WHERE (69.1*([longitude]- "&lon&") * cos("&lat&"/57.3))^2 + (69.1*([latitude]- "&lat&"))^2 < "&radius
Here's the error I receive:
The data types float and int are incompatible in the '^' operator.
I'm unsure of a workaround, can anyone point me in the right direction?
Answer:
Using SQL Server 2008 R2
SELECT city FROM tbliplocationsnew WHERE POWER((69.1*([longitude]- "&lon&") * cos("&lat&"/57.3)),2) + POWER((69.1*([latitude]- "&lat&")),2) < "&radius
Not sure what database you use, but I think that "^2" in SQL does not mean "squared" like in maths. You should use a math "power" function, like POWER(number,2) in SQL Server (since you use VB maybe you use SQL Server ?)
You need to have two of the same data type it's saying. SQL thinks "5" is an int. So, you should be able to trick it into treating it as a float, by putting "5.0" instead.