Call CosmosDB ToString method in LINQ - c#

I have a LINQ query where I need to use the CosmosDB ToString method within a query that is built via LINQ in C#. However I have not been able to figure out how to do this. Simply calling ToString() in the C# LINQ expression on the entity value does not work.
I am trying to do a "contains" search on a number value. I can not change the type, and nor can I add a duplicated field that has the number converted to a string.
Essentially what I need is this (in the where clause):
CONTAINS(ToString(root["MyProperty"]), "MySearchValue")
My current LINQ query is this (MyProperty is of type long):
query.Where(x => x.MyProperty.ToString().Contains("MySearchValue"))
But this generates the following SQL:
CONTAINS(root["MyProperty"], "MySearchValue")
Which does not work since MyProperty is a number, so the Contains method always returns false.
So my question is this: How do I modify my LINQ query to wrap the number value in the ToString() method like in my first code sample above? Or is this even possible currently?

There is actually a bugfix for this issue awaiting merge here: https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3385 Does this apply to your specific situation?

Related

LINQ to Entities does not recognize the method 'Boolean ToBoolean [duplicate]

This question already has an answer here:
LINQ to Entities does not recognize the method 'System.Web.Mvc.FileResult'
(1 answer)
Closed 7 years ago.
I have a class Like this :
public class menu{
public string Permission{get;set;}
}
The value Of Permission is Encripted . I want all records where Permission is True. To do this, I use this query :
return
_menuSettings.Where(row => Convert.ToBoolean(Utilities.Encryption.Decrypt(row.Permission,"key"))==true).ToList();
but I get this error :
LINQ to Entities does not recognize the method 'Boolean ToBoolean(System.String)' method, and this method cannot be translated into a store expression.
I searched on google, but I can't solve it .
thanks
What you are asking for cannot be achieved by db query. I'm afraid you are stuck with in memory filtering (hope your don't have too many records) like this
return
_menuSettings.AsEnumerable().Where(...
here AsEnumerable() will switch the context from Linq to Entities to Linq to Objects
Not every method is convertible to SQL thats the essence of that message.
In your case you can compare against the string "true".
_menuSettings.Where(row => Utilities.Encryption.Decrypt(row.Permission,"key").ToLower()=="true").ToList();
Now as mentioned the message means the method is not convertible to SQL. So then by no real surprise get that Utilities.Encryption.Decrypt is also not supported.
Then continue with the same concept of taking things that don't work, out of the query.
The quick and dirty way is to realize/project the data (use ToList() or ToIEnumerable() before you filter with the non-supported filter).
Meaning that you take everything out of the table and filter it on your server instead on the DBMS (SQL server).
Like this. (i have split it into more lines for readability)
var projection = _menuSettings.ToList();
var result = projection.Where(row => Utilities.Encryption.Decrypt(row.Permission,"key").ToLower()=="true").ToList();
A wise choice is to find a good way to limit the projection size before you do heavy work like this.

LINQ to SQL: "Method 'Boolean Contains(System.String)' has no supported translation to SQL."

I have a hashset of strings representing tmdbId's for movies I have on disk - called moviesOnDisk.
I have a database of movie objects, indexed on the tmdbId.
I want to delete the records that exist in the database but don't exist on disk.
I have this line to get the difference:
var toDelete = Database.Movies.Where(x => !moviesOnDisk.Contains(x.TMDbId));
this gives me no results and the following message:
Method 'Boolean Contains(System.String)' has no supported translation to SQL.
Is there a work around for this? Obviously I can iterate over both lists, but I am going for best performance.
Change your where clause to !moviesOnDisk.ToList().Contains(x.TMDbId).
#Rob provided a great explanation in comments on why Contains will work on IEnumerable, but not on a HashSet:
It works because Contains is a specific implementation on
HashSet. When translating to SQL, it has a set of supported
methods, including Queryable.Contains() - which is a different method
from what you've written. HashSet.Contains has a different
implementation (that is, hashing the value and doing a lookup), and
can't be converted to SQL
change moviesOnDisk to array,it will work.
the type of array item must same as TMDbId

Which methods use in Linq to force a retrieving data from database

When i use this query, i don't retrieve data from database:
var query = db.Table.Where(x => x.id_category.HasValue).Select(x => x.Id);
But if i use this, i'll retrieve data:
var dataRetrieved = query.ToList();
Which others methods, like ToList(), i can force a retrieving data?
LINQ works by building up a series of query commands, and waits until the last possible moment to execute them. In order to force execution, you have to ask the query for "real" data, not just a description of the steps to get it.
According to this MSDN article (emphasis mine):
LINQ queries are always executed when the query variable is iterated over, not when the query variable is created....
To force immediate execution of a query that does not produce a singleton value, you can call the ToList method, the ToDictionary method, or the ToArray method on a query or query variable....
You could also force execution by putting the foreach or For Each loop immediately after the query expression, but by calling ToList or ToArray you cache all the data in a single collection object.
The select returns an IEnumerable, which is a sequence of values to invoke a transform function on. So it depends on you what transformation you want? you want list or array? you can also traverse through and manipulate each piece independently using a foreach loop. Following page will give you an idea of what you can do with your values.
Besides what BJ Myers and Ebad Massod already explained, there are also operators which return single value and that are executed immediately, such as:
First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault
Also, LINQ Aggregate Functions return a single value and are executed immediately, such as:
Average, Count, Max, Min, Sum
Here is a nice MSDN article: Classification of Standard Query Operators by Manner of Execution

LINQ to entities : cannot call a method

I'm aware there are alreay a lot of posts concerning this issue, but i can't seem to find a solution for this.
Here's my Ling to Entities query :
IEnumerable<Tblstamp> changes = (from c in userGSN.edb.Tblstamp
where (c.Ts_Date >= userGSN.DateLastCheck &&
TimeSpan.Parse(c.Ts_Time) >= userGSN.TimeLastCheck)
orderby c.Ts_Id ascending
select c);
I want to compare c.Ts_time to userGSN.TimeLastCheck, but for that I have to convert the c.Ts_Time to a timespan (it is a string, and comes from a database I can't modify, tried everything). I also can't do the converting before the query in an other variable since I can't access it outside of the query.
Obviously, I get an error for trying to use the TimeSpan.Parse method in my query, but I can't find any workaround to this. I have tried using LINQ to Object but since I am really not used to it I couldn't make the equivalent query that i have here.
I am aware of the problem, I'm just trying to find a workaround and need some help please !
EDIT :
So I tried the DateDiff function as suggested :
IEnumerable<Tblstamp> changes = (from c in userGSN.edb.Tblstamp
where (c.Ts_Date >= userGSN.DateLastCheck && SqlFunctions.DateDiff("second",userGSN.TimeLastCheck,c.Ts_Time).Value > 0 )
orderby c.Ts_Id ascending
select c
);
but it gives me the same error : "LINQ to Entities does not recognize the method 'System.Nullable1[System.Int32] DateDiff(System.String, System.Nullable1[System.TimeSpan], System.String)' method, and this method cannot be translated into a store expression."
Even though it clearly says here "You cannot call this function directly. This function can only appear within a LINQ to Entities query.", which is exactly what I'm doing ?!
There's no clean way to do this, so you'll have to think outside the box. Try using SqlFunctions.DateDiff to perform the check. You'll need to adapt it for your usage, but:
SqlFunctions.DateDiff("second", c.Ts_Time, userGSN.TimeLastCheck.ToString()) > 0
There are other methods that you can use, if DateDiff is not suitable. See EntityFunctions as well.
Depending on what you're using to query the data, you might want System.Data.Entity.SqlServer.SqlFunctions instead.
Every code you write in LinQ to get data from database must convert to a valid T-SQL query with valid statements. Not all C# methods have a correspondance in T-SQL so whenever you use them in your code while fatching data you will get errors.
You should either do modifications on the SQL side or use C# spesific functions after fething data from database, while working with entities in memory, or use fıunstions which comply with SQL statements.
If you can't get EF to translate your query to SQL, maybe you could do it yourself? See Writing SQL queries for entities
Linq to Entities does not support all functions since the query has to be translated to SQL:
from c in userGSN.edb.Tblstamp.AsEnumerable()
//now you are allready getting the data from the database
//so be carefull with that because you will have bad performance

LINQ to Entities does not recognize the method 'Int32 IndexOf(System.String, System.StringComparison)' method

I have executed a linq query by using Entityframework like below
GroupMaster getGroup = null;
getGroup = DataContext.Groups.FirstOrDefault(item => keyword.IndexOf(item.Keywords,StringComparison.OrdinalIgnoreCase)>=0 && item.IsEnabled)
when executing this method I got exception like below
LINQ to Entities does not recognize the method 'Int32 IndexOf(System.String, System.StringComparison)' method, and this
method cannot be translated into a store expression.
Contains() method by default case sensitive so again I need to convert to lower.Is there any method for checking a string match other than the contains method and is there any method to solve the indexOf method issue?
The IndexOf method Of string class will not recognized by Entity Framework, Please replace this function with SQLfunction or Canonical functions
You can also take help from here or maybe here
You can use below code sample:
DataContext.Groups.FirstOrDefault(item =>
System.Data.Objects.SqlClient.SqlFunctions.CharIndex(item.Keywords, keyword).Value >=0 && item.IsEnabled)
You really only have four options here.
Change the collation of the database globally. This can be done in several ways, a simple google search should reveal them.
Change the collation of individual tables or columns.
Use a stored procedure and specify the COLATE statement on your query
perform a query and return a large set of results, then filter in memory using Linq to Objects.
number 4 is not a good option unless your result set is pretty small. #3 is good if you can't change the database (but you can't use Linq with it).
numbers 1 and 2 are choices you need to make about your data model as a whole, or if you only want to do it on specific fields.
Changing the Servers collation:
http://technet.microsoft.com/en-us/library/ms179254.aspx
Changing the Database Collation:
http://technet.microsoft.com/en-us/library/ms179254.aspx
Changing the Columns Collation:
http://technet.microsoft.com/en-us/library/ms190920(v=sql.105).aspx
Using the Collate statement in a stored proc:
http://technet.microsoft.com/en-us/library/ms184391.aspx
Instead you can use this method below for lowering the cases:
var lowerCaseItem = item.ToLower();
If your item is of type string. Then this might get you through that exception.
Erik Funkenbush' answer is perfectly valid when looking at it like a database problem. But I get the feeling that you need a better structure for keeping data regarding keywords if you want to traverse them efficiently.
Note that this answer isn't intended to be better, it is intended to fix the problem in your data model rather than making the environment adapt to the current (apparently flawed, since there is an issue) data model you have.
My main suggestion, regardless of time constraint (I realize this isn't the easiest fix) would be to add a separate table for the keywords (with a many-to-many relationship with its related classes).
[GROUPS] * ------- * [KEYWORD]
This should allow for you to search for the keyword, and only then retrieve the items that have that keyword related to it (based on ID rather than a compound string).
int? keywordID = DataContext.Keywords.Where(x => x.Name == keywordFilter).Select(x => x.Id).FirstOrDefault();
if(keywordID != null)
{
getGroup = DataContext.Groups.FirstOrDefault(group => group.Keywords.Any(kw => kw.Id == keywordID));
}
But I can understand completely if this type of fix is not possible anymore in the current project. I wanted to mention it though, in case anyone in the future stumbles on this question and still has the option for improving the data structure.

Categories