Compare Greek (or other non-latin) strings in linq - c#

I am trying to write a query in linq that returns results like this:
PeopleEntities pe = new PeopleEntities();
String fName = "Τάκης";
var people = pe.Person.Where(per => per.FirstName.Equals(fname)).ToList();
When I dump the query using
String query = ((ObjectQuery)pe.Person.Where(per => per.FirstName.Equals(fname))).ToTraceString();
Console.WriteLine(query);
and then using the query in a MySQL WorkBench providing a value, everything works fine but in code the query returns nothing.
Edit: I have connected entity manager by using a MySQL connector. I hadn't thought about it earlier but this is the problem as with an MsSQL connection everything works fine
Any ideas on how I can execute the above?

In a regionally-sensitive query, you should not use string.Equals() without providing a StringComparison value, e.g.:
var people = pe.Person.Where(
per => per.FirstName.Equals(fname, StringComparison.CurrentCultureIgnoreCase));
Yes, this overload is supported in L2E.
If that still does not work, it is possible that the AppDomain's current culture or the DB's collation is wrong. Look at the query/params in SQL Profiler.

Related

StringComparison.InvariantCultureIgnoreCase cannot be translated while used in a LINQ query

I am facing a problem executing the below query in .NET 6.
query = context.Where(user =>
user.Email.Contains(model.Email,
StringComparison.InvariantCultureIgnoreCase));
After searching the web I understood that EF Core does translate Contains for server-side evaluation - but not the overload that accepts StringComparison.InvariantCultureIgnoreCase or any other StringComparison. But never found the correct way to solve this issue
So I changed the query to something as follows to make it work:
query = context.Where(user =>
user.Email.ToLower().Contains(model.Email.ToLower());
Even though it is working I am not entirely happy with this solution and still wondering which solution solves my problem best. Would using ToLowerInvariant() be a better solution? Any better approach to solve this?
UPDATE
ToLowerInvariant() does not work and causes the same error caused by StringComparison.InvariantCultureIgnoreCase
It seems like your are writing your LINQ query on a DbSet. This is not possible as it cannot be translated to SQL statements.
You could however use the EF.Functions.Like function. This gets translated to the SQL provider and is by default case insensitive.
query = context.Where(user =>
EF.Functions.Like(user.Email, model.Email));
How your query reacts depends on the collation you set on the server side. After all your linq expressions will be translated into an SQL query and how that is interpreted will depend on your database and column settings.
What you could try is stating a collation in your query e.g.
var customers = context.Customers
.Where(c => EF.Functions.Collate(c.Name, "latin1_general_ci collation") == "John")
.ToList();
//SQL_Latin1_General_CP1_CI_AS for SQL Server
//latin1_general_ci collation for MySQL
as found in the Microsoft documentation. Where CI stands for case-insensitive (opposed to CS). Be aware that this query won't be able to leverage the index on the Name due to the custom collation. So it would be better to define it on the column (or table/database).
Try this:
query = context.Where(user => EF.Functions.Collate(user.email,
"SQL_Latin1_General_CP1_CI_AI").Contains(model.Email));

ODP.NET / EF6 - CHAR datatype in WHERE clause

I'm starting a project with EF6 and ODP.NET and I'm having trouble performing look-ups based on fixed-length CHAR columns.
The following code returns no results, even though this user exists in the database.
using (var context = new Entities())
{
var search = "testuser";
var result = context.Users
.Where(u => u.UserName == search)
.FirstOrDefault();
}
I know I can get around this by padding the search string or by trimming the database column, but I'm looking for an alternate solution.
I noticed that if I execute the query directly using OracleConnection/OracleCommand, then it works. Is there an attribute or anything I can add to the entity class to cause ODP.NET to bind the variable as an OracleDbType.Char?
Basically, I'm looking for a way to reproduce the following behavior from EF6:
var cmd = new OracleCommand("SELECT * FROM users WHERE user_name = :p0", conn);
cmd.Parameters.Add(":p0", OracleDbType.Char).Value = "testuser";
I also tested with Devart's dotConnect Oracle driver. Using that driver, I can do a look-up successfully by adding the following line of code. I would prefer to use ODP.NET over dotConnect, though. It seems ODP.NET is ignoring this property because it has no effect when using ODP.NET. Is there an equivalent to this that ODP.NET will recognize?
modelBuilder.Entity<Users>()
.Property(u => u.UserName
.IsFixedLength();
If I could, I would just change the column type to VARCHAR2 and wash my hands of this, but unfortunately, that is not an option at the moment. Any help you can provide would be very much appreciated!
replace the Oracle command with
SELECT * FROM users WHERE cast(user_name as varchar2(20)) = :p0
You can use CAST to convert most datatypes to most other datatypes in Oracle. A CHAR datatype is nasty to work with (it's deprecated for a reason) and converting it to varchar2(xx) is the best option.

MySql Linq-Entities Date Comparison

I am trying to do the following in MySql but get a linq-to-entities exception that the functions are not supported. I've tried using SqlFunctions.DateAdd unsuccessfully. The only solution so far is to use a ToList() but that brings it all to the client which I don't love:
var expiredPolcies = context.ApiUsagePolicies
.Where(u => DateTime.UtcNow > u.UsagePolicyStart.AddSeconds(u.UsagePeriodSeconds));
SqlFunctions is specific to Entity Framework and SQL Server.
SqlMethods is specific to Linq to SQL and SQL Server.
If there's an equivalent set of methods for MySql then you could use that.
Alternatively try rewriting your query so that there are no local methods involved. Try something like:
var expiredPolcies = context.ApiUsagePolicies
.Where(u =>
(DateTime.UtcNow - u.UsagePolicyStart).TotalSeconds > u.UsagePeriodSeconds);
It all depends on what functions your Linq provider supports.

Calling a UDF with Entity SQL but without a FROM clause?

Am trying to call a UDF which just takes a parameter and returns a scalar. The examples I've seen all use a From clause: http://blogs.microsoft.co.il/blogs/gilf/archive/2009/10/20/calling-user-defined-functions-udfs-in-entity-framework.aspx
But I only want to call the UDF, I don't want to "join" it with any entities using a from clause:
string fieldTag = "TagNameHere";
var sql = "SELECT XyzModel.Store.FieldNameToFormIdMap(#fieldTag)";
System.Data.Objects.ObjectQuery<int> query =
new System.Data.Objects.ObjectQuery<int>(sql, xyzEntitiesContext);
query.Parameters.Add(new System.Data.Objects.ObjectParameter("fieldTag", fieldTag));
I get the error: "The query syntax is not valid."
This is using .NET 3.5
I could use old style ADO.NET but I'd rather not have to manage another set of connection strings aside from those used for the entity framework.
Just remove SELECT from your query, like this:
var sql = "XyzModel.Store.FieldNameToFormIdMap(#fieldTag)";

How do I view the SQL generated by SubSonic SimpleRepository?

I've got this toy code, works fine, using MySQL
var r = new SimpleRepository("DB", SimpleRepositoryOptions.None);
var q = r.Find<User>(x => x.UserName == "testuser");
How do I view the SQL generated by that query ?
For SQL Server, you can always run SQL Profiler to see the queries.
Unfortunately using SimpleRepository you can't do what you want without stepping into the SubSonic code. Because the Find method returns an IList it's executed before you get the chance to evaluate the SQL that's going to be executed. There are efforts underway to add this functionality in future versions of SubSonic but until then you're probably best looking at the MySQL Query Profiler.

Categories