EF 4.1 Linq Predicate Contains Generates desired SQL when using hard-coded string but not with string variable - entity-framework

I have this code:
string name ="M";
Expression<Func<Organization, bool>> getExpression1 = r => r.Name.Contains(name);
Expression<Func<Organization, bool>> getExpression2 = r => r.Name.Contains("M");
GetOrgListBy(getExpression);
GetOrgListBy(getExpression2);
public IEnumerable<Organization> GetOrgListBy(Expression<Func<Organization, bool>> filter)
{
return DataContext.Parties.OfType<Organization>().Where(filter).ToList();
}
Question:
I am passing a simple expression to a repository function
that gets List<Organization> by matching the name passed.
When I view the sql trace for getExpression1 (uses a string variable), the expression generates a [Name] like plinq_variable sql statement
which omits the % operator. My intention is to generate a
[Name] like '%' + plinq_variable + '%' sql statement
However, when I when using getExpression2 (hard-coded the string), the expression successfully
generates a [Name] like N'%M%' sql statement.
Why doesn't getExpression1 generate the % operator?
How can I make it generate a % operator in the sql just like getExpression2?

The first expression translates into a parametrized SQL query. The variable in the Where clause ...
[Name] like plinq_variable
... is set to the value
#plinq_variable=N'%M%'
when the query is executed. The second expression translates to a SQL query with a hardcoded string literal:
[Name] like N'%M%'
Then this query is directly executed.
The result for both expressions and queries is the same.

Related

Can I cast the result of plainto_tsquery to a string using npgsql / ef core to allow searching by part of a word or phrase?

My end goal is to be able to do a prefix search in npgsql using ef core, very similar to Npgsql Entity Framework Core PostgreSQL full text search by part of word. And Match a phrase starting with a prefix with full text search explains what the query will look like, something like:
select * from mytable
where
mycolumn ## to_tsquery(CONCAT(CAST(plainto_tsquery('the query') as text), ':*'))
In dotnet, I can get close to this, but not exactly what I want.
Using "Matches" converts to plainto_tsquery:
var results= MyTable
.Where(c => c.MyColumn.Matches("the query"))
.ToList();
which results in:
select * from mytable
where
mycolumn ## plainto_tsquery('query')
Using EF.Functions.ToTsQuery converts to to_tsquery (closer):
var results= MyTable
.Where(c => EF.Functions.ToTsQuery("the query")))
.ToList();
which results in:
select * from mytable
where
mycolumn ## to_tsquery('the query')
And now I want to use to_tsquery, passing in the results of plainto_tsquery and appending the ":*" to make it a prefix, but I can't figure out how to convert plainto_tsquery results to a string. I am trying the following:
var results= MyTable
.Where(c => c.SearchVector.Matches(
EF.Functions.ToTsQuery(
EF.Functions.PlainToTsQuery("the query") + ":*")))
.ToList();
But this fails with:
System.InvalidCastException: Unable to cast object of type 'System.String' to type 'NpgsqlTypes.NpgsqlTsQuery'.
I think I just need to be able to cast the results of EF.Functions.PlainToTsQuery("the query") to a string, but I am not sure how to do that or if possible.
Appreciate any help.
Thanks,
Eric
I was not able to figure out how to cast the results of EF.Functions.PlainToTsQuery("the query") to text, but I was able to solve my issue by creating a stored function which creates the tsquery. And then calling the stored function via ef core.
The stored function is something like the following:
CREATE OR REPLACE FUNCTION public.get_ts_query_for_prefix_search(
query_text text)
RETURNS tsquery
LANGUAGE 'sql'
AS $BODY$
SELECT
CASE plainto_tsquery('simple', query_text) = ''
WHEN true THEN ''
ELSE to_tsquery('simple', CAST(plainto_tsquery('simple', query_text) AS TEXT) || ':*')
END
$BODY$;
And the ef core where clause will be something like the following:
var matches = MyTable
.Where(w => w.MyColumn.Matches(GetTsQueryForPartialSearch(queryText));

How to use replace query in yii2 updateAll()?

I am using Postgresql. My Yii2 code for the update is
ModelName::updateAll(['my_column' => "REPLACE(my_column1,'removed_','')"]);
Actual query is
update my_table set my_column = REPLACE(my_column1,'removed_','');
When I run my yii2 code it shows the error
SQLSTATE[22001]: String data, right truncated: 7 ERROR: value too long for type character varying(50)
The SQL being executed was: UPDATE "my_table" SET "my_column1"='REPLACE(my_column1,''removed_'','''')'
If you use ['column' => 'value'] syntax for attributes the framework expects that values of array are simple values and treats them accordingly. That's why your expression gets converted to string value instead of using as expression.
If you want to avoid that you need to wrap your values in yii\db\Expression like this:
ModelName::updateAll([
'my_column' => new \yii\db\Expression("REPLACE(my_column1,'removed_','')")
]);

How use '?' in #Query? How use Jsonb of postgres in spring-boot at all?

How to use ?| operator in postgres query in spring repository? I need to use where in my query for text type column which content json.
#Query(value =
"SELECT * \n" +
"FROM tbl t \n" +
"WHERE t.some_ids::::jsonb ?| array['152960','188775']", nativeQuery = true
)
List<Model> getModelsByIds();
But that don't work and I catch the next exeception:
org.springframework.dao.InvalidDataAccessApiUsageException: At least 1 parameter(s) provided but only 0 parameter(s) present in query.
You can use the associated function of that operator instead. Most of the time the obfuscation layers also choke on the :: cast operator, so you might want to use cast() instead:
WHERE pg_catalog.jsonb_exists_any(cast(t.some_ids as jsonb), array['152960','188775'])
However I think this wouldn't be able to make use of an index defined on some_ids::jsonb
You didn't mention how exactly the content of some_ids looks like.
If that is a JSON array (e.g. '["123", "456"]'::jsonb) then you can also use the contains operator #>:
WHERE cast(t.some_ids as jsonb) #> '["152960","188775"]'
If your JSON array contains numbers rather than strings ('[123,456]') you need to pass numbers in the argument as well:
WHERE cast(t.some_ids as jsonb) #> '[152960,188775]'

How to format a number in Entity Framework LINQ (without trailing zeroes)?

In a SQL Server database I have a column of decimal datatype defined something like this:
CREATE TABLE MyTable
(
Id INT,
Number DECIMAL(9, 4)
)
I use Entity Framework and I would like to return column Number converted to a string with only the digits right of the decimal separator that are actually needed. A strict constraint is that a result must be an IQueryable.
So my query is:
IQueryable queryable = (
from myTable in MyDatabase.NyTable
select new
{
Id = myTable.Id,
Number = SqlFunctions.StringConvert(myTable.Number,9,4)
}
);
The problem with is that it always convert number to string with 4 decimals, even if they are 0.
Examples:
3 is converted to "3.0000"
1.2 is converted to "1.2000"
If I use other parameters for StringConvert i.e.
SqlFunctions.StringConvert(myTable.Number, 9, 2)
the results are also not OK:
0.375 gets rounded to 0.38.
StringConvert() function is translated into SQL Server function STR.
https://learn.microsoft.com/en-us/sql/t-sql/functions/str-transact-sql?view=sql-server-2017
This explains the weird results.
In the realm of Entity Framework and LINQ I was not able to find a working solution.
What I look for is something like C# function
String.Format("0.####", number)
but this cannot be used in a LINQ query.
In plain simple SQL I could write my query like this
SELECT
Id,
Number = CAST(CAST(Number AS REAL) AS VARCHAR(15))
FROM
MyTable
I have not managed to massage LINQ to produce query like that.
A workaround would be to forget doing this in LINQ, which is quite inflexible and messy thing, borderline on useless and just return type DECIMAL from database and do my formatting on a client side before displaying. But this is additional, unnecessary code and I would hate to di it that way if there perhaps is a simpler way via LINQ.
Is it possible to format numbers in LINQ queries?
I would absolutely return a decimal from he database and format it when needed. Possible directly after the query. But usually this is done at display time to take into account culture specific formatting from the the client.
var q =
(from myTable in MyDatabase.NyTable
select new
{
Id = myTable.Id,
Number = myTable.Number
})
.AsEnumerable()
.Select(x => new { Id = x.Id, Number = x.Number.ToString("G29") });

Entity Framework search expression

I have to make search through field EmployeeId.
I also have parameter empId which can be something like this: '123'
In my database there's next values:
'0123' - what I need
'10123'
'1234'
and so on.
My target - take a record which ends on empId and has no or one and more leading 0.
I tried to write something like this:
var result = from member in MembersSet
where SqlMethods.Like(member.EmployeeId, "%" + empId) &&
!(SqlMethods.Like(member.EmployeeId, "%[^0]%" + empId))
select member.Id;
But I get an error
LINQ to Entities does not recognize the method 'Boolean Like(System.String, System.String)' method, and this method cannot be translated into a store expression.
May be there is a workaround?
The SqlMethods is for Linq-To-Sql, not EntityFramework. There's an equivalent for EF - SqlFunctions (System.Data.Objects.SqlClient.SqlFunctions). You can use the PatIndex function to do what you want. It's worth noting that this will make your code SQL Server specific.
You can do the initial expression without using PatIndex by using the standard String EndsWith function, which will get translated by EF automatically.
var result = from member in MembersSet
where member.Id.EndsWith("123")
&& SqlFunctions.PatIndex("[^0]%123", member.Id) == 0
select member.Id;
If the values of Id will always be numeric (even with leading zeros), you could try converting them to Integers on the server, by defining an additional method to handle parsing to int (EF won't translate Int32.Parse) - see this question for details.