LINQ ; Search with culture invariant - c#

Here is my problem. We've got a list of enterprises, users or anything and we must search into it with a "StartsWith" search type. So on our site, we ain't got a Search field such as a Textbox but we've got a search header including 27 buttons "#", "A", "B", "C", [...] "Z".
The problem we have is that if user click on "E" button, when we querying to get value from database, entreprises name may start with an "É", "È", "Ê", bacause yes, our site is in french. Any idea of how to do it in LINQ.
This is important to know too that we're using LLBLGen Pro. So I guess it need to have something he can convert into a valid SQL Query.
Here is what we've already tryied :
IList<Enterprise> enterprises;
switch (searchChar){
[...]
case "E":
enterprises = from ent in ourContext.Enterprises
where "eèéêë".Any(param => ent.name[0] == param)
select ent;
break;
[...]
}
Which give us this error something relatively to a unconvertable query:
Unable to cast object of type 'System.Linq.Expressions.ConstantExpression' to type 'SD.LLBLGen.Pro.LinqSupportClasses.ExpressionClasses.SetExpression'.
So we've tried to make it basicaly with a simple LINQ query, without querying to DB to know if it's possible.
IList<string> test = new List<string>() { "École", "enlever", "avoir" };
IList<string> result = (from value in test
where "eéèêë".Contains(value[0].ToString())
select value).ToList();
What is weird with this query, is that it ain't crash. But, it ain't work too! When debugging, it go throught it, but when we try to see what is into "result" list, it's like if there's nothing in it. I mean, the list is simply null. But nothing fail into the try catch!
Please help !

The real solution is here is to create an extra column in your database for a searchable name, so whenever you add a new company you also add a value to the searchable name column. You would convert all characters to upper (or lower if you like) invariant and add the "clean" name to the searchable name column. You could also remove punctuation at this point too, as that is often a pain in searches.
This will leave you with a column you will never display but it will be much easier (and also much quicker) to search for matches in this column as you will never need to worry about accented characters at search time.

Just use StartsWith method of the string
IList<string> test = new List<string>() { "École", "enlever", "avoir" };
var result = test
.Where(s => s.StartsWith("e", StringComparison.CurrentCultureIgnoreCase))
.ToList();

If i got you right here is what you want:
var result = test.Where(x => "eéèêë".Contains(Char.ToLowerInvariant(x.FirstOrDefault())));

The Any seem to be not working. Use Contains instead. This is workin.
enterprises = from ent in ourContext.Enterprises
where "eèéêëEÈÉÊËE".Contains(ent.name[0])
select ent;

I'm not sure whether you have any control over the database, and which RDMBS you are using, but an easy way seems to be using a case insensitive, accent insensitive collation in your query - this way SQL does the hard work.
-- Assuming that your existing table is Accent Sensitive
create table Enterprises
(
name nvarchar(200) collate SQL_Latin1_General_CP1_CI_AS
)
GO
-- We can still use an Accent Insensitive View
create view vEnterprises
as
select name collate SQL_Latin1_General_CP1_CI_AI AS name
from Enterprises
GO
insert into Enterprises values ('e')
insert into Enterprises values ('è')
insert into Enterprises values ('é')
insert into Enterprises values ('ê')
insert into Enterprises values ('ë')
insert into Enterprises values ('E')
insert into Enterprises values ('É')
insert into Enterprises values ('È')
insert into Enterprises values ('Ê')
-- returns 'e' and 'E'
select * from Enterprises where name like 'e%'
-- returns everything
select * from vEnterprises where name like 'e%'
i.e. Assuming that you can add the accent insensitive view to LLBLGen you can just pass 'e' to the query.

Related

Create SQL query with WHERE .. IN condition using list of integer

I am not an expert of SQL and what I am trying to achieve is the following.
I have a list of integers called PersonIds. First of all I need to transform this list of integers to a list of strings. Why? Because as far as I have understood for the WHERE .. IN condition in SQL this is the kind of variable I need to feed to the query.
Then I need to modify the list of strings in order to prevent SQL injection and therefore inserting # before every Id of the list.
At last I create the query and feed the list I just created.
What I tried to do is:
var listIds = string.Join(",#", PersonIds, 0, PersonIds.Count()));
var query = $"DELETE FROM PersonTable WHERE PersonId IN (#{listIds})";
There is something I am doing wrong. To recap I need to steps:
preparing a list (including mechanism to avoid SQL injection) that I need to feed to the query
create the query using as argument the list I created
Thanks in advance!
Perhaps the simplest way is to add some Dapper:
List<int> listIds = ...
connection.Execute("DELETE FROM PersonTable WHERE PersonId IN #listIds",
new { listIds });
Dapper does all the hard work of figuring out how to parameterize that, while still staying almost close to regular TSQL. You can also optionally enable string_split usage if you're using recent versions of SQL Server, to minimize the parameter count and query-plan cache size.
Note that the missing parentheses is deliberate and intentional - dapper treats this slightly differently to the regular IN (#foo, #bar) syntax.

Linq query not behaving as expected

I have a very simple linq query which is as following:
var result = (from r in employeeRepo.GetAll()
where r.EmployeeName.Contains(searchString)
|| r.SAMAccountName.Contains(searchString)
orderby r.EmployeeName
select new SelectListItem
{
Text = r.EmployeeName,
Value = r.EmployeeName
});
The issue is for some strange reason it fetches me the record of every person who I search for whether in lower case or upper case. i.e.
test user
Test User
TEST USER
I will get back the correct records. However when I search for my own name using lower case I don't get any results back but if I use the first letter of my name as upper case then I get the results. I can't seem to figure out why its doing that.
Every first and last name in the database start with upper case.
The searchString which I'm using are:
richard - I get correct results
waidande - no results found
Both of the above users are in the database.
I'm also using Entity Framework to query Sql Server 2012.
If your text has NVARCHAR datatype check for similiar letters that in reality are not the same:
CREATE TABLE #employee (ID INT IDENTITY(1,1), EmployeeName NVARCHAR(100));
INSERT INTO #employee(EmployeeName) VALUES (N'waidаnde');
SELECT *
FROM #employee
WHERE EmployeeName LIKE '%waidande%';
-- checking
SELECT *
FROM #employee
WHERE CAST(EmployeeName AS VARCHAR(100)) <> EmployeeName;
db<>fiddle demo
Here: 'а' != 'a'. One is from Cyrillic 'a' and the second is normal.
Idea taken from:
Slide from: http://sqlbits.com/Sessions/Event12/Revenge_The_SQL
P.S. I highly recommend to watch Rob Volk's talk: Revenge: The SQL!.
To troubleshoot the issue, determine whether the problem is on the EF side, or on DB side.
A common mistake is extra whitespace, so make sure it's not the case before proceeding.
First check what query is being generated by EF, you can use one of the following methods to do this
ObjectQuery.ToTraceString() method
EF logging of intercepted db calls
Sql server profiler
If you are using EF correctly and your query is translated to SQL as expected and contains the predicates in the where section, but you still are not getting any meaningful results, here are some ideas to try out on the DB side:
Check collation ( be aware it can be set on server, database and individual column level) - beware of case sensitivity and code page that is being used
Verify that your search string contains symbols that can be interpreted in the db code page - for example if code page is 252 - Windows Latin 1 ANSI and you are sending input with symbols from UTF-16 that are outside ANSI - you won't get any results, even though the symbols look the same
Highly improbable, but as last resort check if one of your queries has not been cached, as described here
SQL Server 2012 (SQL Server) is installed by default with case insensitive collation. If you need to retrieve records from the database using case sensitivity (because you have "several" records) you need to change the collation (take care because if you change DBMS collation you change also master database collation so also tables and field names become case sensitive).
If you don't need to avoid to retrieve all the records from the DBMS you can just filter records after you retrieve them, i.e.
var result = (from r in employeeRepo.GetAll()
where r.EmployeeName.Contains(searchString)
|| r.SAMAccountName.Contains(searchString)
orderby r.EmployeeName
select new SelectListItem
{
Text = r.EmployeeName,
Value = r.EmployeeName
})
.ToList() // Materialize records and apply case sensitive filter
.Where(r.EmployeeName.Contains(searchString)
|| r.SAMAccountName.Contains(searchString));

What ways exist with linq to compare if a case insensitive and culture using string exists within another

Comparing case insensitive strings with linq is quite easy, even to find out if a specific string is within another. The problems only start when it is also needed (as in my case) to differentate between ss and ß. As far as I have seen linq and string offer only 1 viable option there: Contains but the problem is the only contains overload that takes for example: StringComparer.CurrentCultureIgnoreCase as parameter does not take string as parameter for the part that is searched within the "calling" string but instead takes ONLY a char value.
As it was asked: I'm using linq to gather info from a SQL database thus:
var results = (from c in myEntity.myTablename where
(c.MyStringTextColumn.Contains(myStringTextToCompareWith))
select c.MyStringTextColumn).Distinct().ToList();
is how I'm comparing the strings originally but like I said the problem is this does not differentiate between ss and ß. Thus in the database there are different versions in regards to ss and ß stored, and I need to only find the "correct" one. Even though they are essentially the same one, an example would be strasse and straße (as names are stored in the database I couldn't use them in this example). If I type in strass I only want to find those strings that contain ss and not those with ß (thus straße should not be found)
So my question is: What options exist there?
You should be able to make use of the IndexOf property, which returns a non-negative int if the substring is found (it returns the index of the first character if found, otherwise -1), and also takes a StringComparison argument.
http://msdn.microsoft.com/en-us/library/ms224425(v=vs.110).aspx
Linq to sql doesn't support culture / case sensitive comparison, but it will rely on the database collation and the column type.
Queries do not account for SQL Server collations that might be in
effect on the server, and therefore will provide culture-sensitive,
case-insensitive comparisons by default. This behavior differs from
the default, case-sensitive semantics of the .NET Framework. -
MSDN
One option that you probably have is creating a stored procedure to do the searching like.
create proc sp_TableName
(#name as varchar(max)) -- notice the type is varchar, not nvarchar
as
select * from TableName
where
cast(ColumnName as varchar(max)) COLLATE SQL_Latin1_General_CP1_CI_AS
like '%' + #name + '%'
And using SqlQuery<T> to get the result.
var db = ...; // the context
var param = new SqlParameter("name", "ß");
var results = db.Database.SqlQuery<TableModel>("exec sp_TableName #name", param)
.Distinct()
.ToList();
Or you can just execute all the data first, so that you can do the comparing as linq to objects, not linq to sql.
var results = (from c in myEntity.myTablename.AsEnumerable() where
(c.MyStringTextColumn.Contains(myStringTextToCompareWith))
select c.MyStringTextColumn).Distinct().ToList();

SELECT * Not returning all rows, unless I ORDER BY id DESC

Application presented with "Sequence Contains More Than One Entity" error. Knowing this is usually the result of a .SingleOrDefault() command in Linq, I started investigating. I can verify that the production server has many instances of duplicate keywords, so that's where I begin.
I have the following Keyword table:
id INT (NOT NULL, PRIMARY KEY)
text NVARCHAR(512)
active INT
active is just a way to "enable/disable" data if the need strikes me. I'm using LINQ to SQL and have the following method implemented:
public Keyword GetKeyword(String keywordText)
{
return db.Keywords.SingleOrDefault(k => (k.text.ToUpper() == keywordText.ToUpper()));
}
The idea is that I attach keywords through an association table so that multiple objects can reference the same keyword. There should be no duplicate text entries within the Keyword table. This is not enforced on the database, but rather through code. This might not be best practice, but that is the least of my problems at the moment. So when I create my object, I do this:
Keyword keyword = GetKeyword(keywordText)
if(keyword == null)
{
keyword = new Keyword();
keyword.text = keywordText;
keyword.active = Globals.ACTIVE;
db.Keywords.InsertOnSubmit(keyword);
}
KeywordReference reference = new KeywordReference();
reference.keywordId = keyword.id;
myObject.KeywordReferences.Add(reference);
db.SubmitChanges();
this code is actually paraphrased, I use a repository pattern, so all relevant code would be much longer. However, I can assure you the code is working as intended, as I've extensively tested it. The problem seems to be happening on the database level.
So I run a few tests and manual queries on my test database and find that the keyword I passed in my tests is not in the database, yet if I do a JOIN, I see that it is. So I dig a little deeper and opt to manually scan through the whole list:
SELECT * FROM Keyword
return 737 results. I decided I didn't want to look through them all, so I ORDER BY text and get 737 results. I look for the word I recently added, and it does not show up in the list.
Confused, I do a lookup for all keywordIds associated with the object I recently worked on and see a few with ids over 1,000 (the id is set to autoincrement by 1). Knowing that I never actually delete a keyword (hence the "Active" column) so all ids from 1 to at least those numbers over 1,000 should all be present, I do another query:
SELECT * FROM Keyword ORDER BY id
returns 737 results, max ID stops at 737. So I try:
SELECT * FROM Keyword ORDER BY id DESC
returns 1308 rows
I've seen disparities like this before if there is no primary key or no unique identifier, but I have confirmed that the id column is, in fact, both. I'm not really sure where to go from here, and I now have the additional problem of about 4,000+ keywords on production that are duplicate, and several more objects that reference different instances of each.
If you somehow can get all your data then your table is fine, but index is corrupted.
Try to rebuild index on your table:
ALTER INDEX ALL ON Your.Table
REBUILD WITH (FILLFACTOR = 80, SORT_IN_TEMPDB = ON, STATISTICS_NORECOMPUTE = ON);
In rare situations you can see such errors if your hard drive fails to write data due to power loss. Chkdsk should help

sql query with alias name

i have a table with this columns--- Or
orgid ispaid validity noofthingstoTake
1 yes 2010-06-05 20
2 yes 2010-06-09 7
i have used this query(to join two more tableS):
select distinct B.RequirementID,A.OrganizationID
from
Organization A,RequirementsDetailsforOrganization B,validityorgdet F
where A.OrganizationID=B.OrganizationID and F.orgid=A.OrganizationID and
F.ispaid=1 and F.validity>=GETDATE() and
F.noofthingstoTake> ??
but i dont know how to check the (noofthingstaken) over here. it should not exceed 20. im passing this query from my code behind page to the Sql. how to get the query excute to check it should not exceed the noofthingstaken
pls help me out....????
Try this
select distinct B.RequirementID,A.OrganizationID from
Organization A,RequirementsDetailsforOrganization B,validityorgdet F
where A.OrganizationID=B.OrganizationID and F.orgid=A.OrganizationID and
F.ispaid=1 and F.validity>=GETDATE() and F.noofthingstoTake <= 20
Presumably noofthingstoTake is actually an alias and not a column name in your table. You can't use column aliases outside of the select clause because they don't actually exist until the query is done running. So, you can't compare directly to noofthingstoTake, but must instead refer to the actual field name that that column came from. If it's an expression, just use the entire expression. Note that if it's an aggregate, you'll need to put it in a having clause, not a where clause.
(Note: You really should have posted your entire query)

Categories

Resources