Use contains in LINQ to SQL join - c#

How can I do a LINQ to SQL join without an exact match? For example, say I have a table form with the data John Smith (2) and I want to join it to the field Smith in table name. Something like this
var query =
from f in db.form
join n in db.name
on f.nameField like '%' + n.firstName + '%'
Although the like keyword doesn't seem to be available to me.

You can't use like in a Linq join. In fact, you can't use like in Linq at all, only conventional string methods like StartsWith, EndsWith, or Contains.
You'd have to do something like this:
var query =
from f in db.form
from n in db.name.Where(x => f.nameField.Contains(x.firstName))
...

Actually, there is a way of doing it, but it's not as neat as using the standard linq stuff:
from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;
(borrowed from the user L P's answer in the linked question)

Related

How can i convert this query to Linq?

i want to convert this sql Query to Linq in C#
select distinct(E.Nombre), E.Puesto,E.Telefono,E.Correo1,E.Correo2
from dbo.CCEscalaMando E
Example output
How can i do?
Let's start off with your SQL:
select distinct(E.Nombre), E.Puesto,E.Telefono,E.Correo1,E.Correo2
from dbo.CCEscalaMando E
Which technically is valid, but the way it is written makes it look like it is doing something that it isn't. The above will execute the exact same as
SELECT DISTINCT E.Nombre, E.Puesto, E.Telefono, E.Correo1, E.Correo2
FROM dbo.CCEscalaMando E
The parenthesis make it look like you are calling a distinct function, when in reality you are just using SELECT DISTINCT, and the first column you just put in parenthesis for no reason. You could put all the columns in parenthesis, but it has effect on the distinct:
select distinct (E.Nombre), (E.Puesto), (E.Telefono), (E.Correo1), (E.Correo2)
from dbo.CCEscalaMando E
Now that we've removed your confusing bit, it's easy to convert:
dbContext.CCEscalaMando.Select(e=>new {
e.Nombre,
e.Puesto,
e.Telefono,
e.Correo1,
e.Correo2})
.Distinct();

LINQ to SQL, condition on foreign entities

In SQL, it'd be done as such:
SELECT * FROM Student
WHERE SchoolId IN
(SELECT id FROM School WHERE Name LIKE '%elementary%')
How do I implement this with LINQ? I've tried the following:
var list = context.Students.Where(x => context.Schools.Where(r => r.Name.Contains("elementary").Select(r => r.Id).Contains(x.SchoolId))
but it's not giving me what I want, unfortunately...
I know it's possible to retrieve all the Ids from the School table first, but I think it'd take a heavy toll on the performance. Preferably I'd like LINQ to SQL to handle everything; I can't do this using vanilla SQL because I need stuff to be dynamic and currently LINQ is the best solution for me.
The code above is all for illustration purposes; what I'm doing is a tad different (but more or less the same). I really do need some help on this; if you need any more information just feel free to ask.
EDIT: My bad, I missed out a field. It works, but the results didn't show up because I was missing that field... So sorry...
Try this:
var result = from st in context.Student
from sc in context.Schools
where sc.Name.Contains("elementary") && sc.SchoolId == st.SchoolId
select st;
I am a bit hazy on the syntax, pardon me. But this should point you to the right direction.
Something like this should work. The first use of Contains is on a string object to see if the string contains the substring "elementary". The second use of Contains is on a list and checks to see if the first result list contains SchoolId.
var sublist = from s in context.Schools
where s.Name.Contains("elementary")
select id;
var list = from s in context.Students
where sublist.Contains(s.SchoolId)
select s;

Full Text Search with LINQ, decrease results with each keyword

I followed the directions in this SO question to create a linq function to do a full text search on a food database i have. there are over 7000 records in this DB and with each keyword i add the results get larger as opposed to smaller.
here is the meat of my function:
SELECT *
FROM USDA_Foods AS FT_TBL
INNER JOIN FREETEXTTABLE(USDA_Foods,
Shrt_Desc,
#searchWord) AS KEY_TBL
ON FT_TBL.foodId = KEY_TBL.[KEY]
How would i decrease the results with each new keyword by altering the code above?
Thanks
You get more results because it works looking for any of the words in the searched column, and not that contains all of the words, as you expect. You can't use AND or anything in FREETEXTTABLE, so you have to use something like CONTAINSTABLE, which allows you to use AND between the provided words.
CONTAINSTABLE (Transact-SQL)
Look at the example "I. Using CONTAINS with a logical operator (AND)" here:
CONTAINS (Transact-SQL)
The syntax is valid also for CONTAINSTABLE.
SELECT *
FROM USDA_Foods AS FT_TBL
INNER JOIN CONTAINSTABLE(USDA_Foods,
Shrt_Desc,
#searchWord) AS KEY_TBL
ON FT_TBL.foodId = KEY_TBL.[KEY]
Your #searchWord should look like
'here AND there AND everywhere'
to look for text that contains here, there and everywhere.
Concatenate INNER JOINS for each search word.
SELECT *
FROM USDA_Foods AS FT_TBL
INNER JOIN FREETEXTTABLE(USDA_Foods,
Shrt_Desc,
#searchWord1) AS KEY_TBL1
ON FT_TBL.foodId = KEY_TBL1.[KEY]
INNER JOIN FREETEXTTABLE(USDA_Foods,
Shrt_Desc,
#searchWord2) AS KEY_TBL2
ON FT_TBL.foodId = KEY_TBL2.[KEY]

Sort Linq list with one column

I guess it should be really simple, but i cannot find how to do it.
I have a linq query, that selects one column, of type int, and i need it sorted.
var values = (from p in context.Products
where p.LockedSince == null
select Convert.ToInt32(p.SearchColumn3)).Distinct();
values = values.OrderBy(x => x);
SearchColumn3 is op type string, but i only contains integers. So i thought, converting to Int32 and ordering would definitely give me a nice 1,2,3 sorted list of values. But instead, the list stays ordered like it were strings.
199 20 201
Update:
I've done some tests with C# code and LinqPad.
LinqPad generates the following SQL:
SELECT [t2].[value]
FROM (
SELECT DISTINCT [t1].[value]
FROM (
SELECT CONVERT(Int,[t0].[SearchColumn3]) AS [value], [t0].[LockedSince], [t0].[SearchColumn3]
FROM [Product] AS [t0]
) AS [t1]
WHERE ([t1].[LockedSince] IS NULL)
) AS [t2]
ORDER BY [t2].[value]
And my SQL profiler says that my C# code generates this piece of SQL:
SELECT DISTINCT a.[SearchColumn3] AS COL1
FROM [Product] a
WHERE a.[LockedSince] IS NULL
ORDER BY a.[SearchColumn3]
So it look like C# Linq code just omits the Convert.ToInt32.
Can anyone say something useful about this?
[Disclaimer - I work at Telerik]
You can solve this problem with Telerik OpenAccess ORM too. Here is what i would suggest in this case.
var values = (from p in context.Products
where p.LockedSince == null
orderby "cast({0} as integer)".SQL<int>(p.SearchColumn3)
select "cast({0} as integer)".SQL<int>(p.SearchColumn3)).ToList().Distinct();
OpenAccess provides the SQL extension method, which gives you the ability to add some specific sql code to the generated sql statement.
We have started working on improving this behavior.
Thank you for pointing this out.
Regards
Ralph
Same answer as one my other questions, it turns out that the Linq provider i'm using, the one that comes with Telerik OpenAccess ORM does things different than the standard Linq to SQL provider! See the SQL i've posted in my opening post! I totally wasn't expecting something like this, but i seem that the Telerik OpenAccess thing still needs a lot of improvement. So be careful before you start using it. It looks nice, but it has some serious shortcomings.
I can't replicate this problem. But just make sure you're enumerating the collection when you inspect it. How are you checking the result?
values = values.OrderBy(x => x);
foreach (var v in values)
{
Console.WriteLine(v.ToString());
}
Remember, this won't change the order of the records in the database or anywhere else - only the order that you can retrieve them from the values enumeration.
Because your values variable is a result of a Linq expression, so that it doest not really have values until you calling a method such as ToList, ToArray, etc.
Get back to your example, the variable x in OrderBy method, will be treated as p.SearchColumn3 and therefore, it's a string.
To avoid that, you need to let p.SearchColumn3 become integer before OrderBy method.
You should add a let statement in to your code as below:
var values = (from p in context.Products
where p.LockedSince == null
let val = Convert.ToInt32(p.SearchColumn3)
select val).Distinct();
values = values.OrderBy(x => x);
In addition, you can combine order by statement with the first, it will be fine.

Dynamic Parameter Count for SQL with C#

So I was thinking about creating a dynamic sql question, meaning that i want the amount of parameters to be dynamic.
Having looked at this: Parameterize an SQL IN clause i was thinking that using like '%x%' is SLOW and not good.
What i actually would like to do is using the IN keyword and have it somewhat like this:
select UserId from NameTable where Name IN (#List_of_names)
Where the #List_of_names could contain i.e.
Filip Ekberg
Filip
Ekberg Filip
Ekberg
Johan Ekberg
Johan
( my second name is johan, thats why it's in there ,) )
So all these should match up with Johan Filip Ekberg.
I want to be using either LINQ to SQL or LINQ to SQL ( Stored Procedure ) using C#.
Suggestions and thoughts please!
----------------- Clearification of scenario and tables -------------------------
Imagine i have the following: A table with Users, A table with Names and a Table that connects the Names to a certain user.
So by having
[User Table]
User ID Full Name
1 Johan Filip Ekberg
2 Anders Ekberg
[Names]
Name ID Name
1 Filip
2 Ekberg
3 Johan
4 Anders
[Connect Names]
Name ID User ID
1 1
2 1
3 1
2 4
2 2
So if i want to look for: Ekberg
The return should be:
Johan Filip Ekberg
Anders Ekberg
If i want to search for Johan Ekberg
Johan Filip Ekberg
If i want to search for Anders
Anders Ekberg
The amount of "search names" can be infinite, if i have the name: Johan Anders Carl Filip Ekberg ( This is just 1 person with many names ) and i just pass "Carl Filip" in, i want to find that User.
Does this make everything clearer?
It looks like SQL 2008 is an option, so passing a table parameter is probably your best bet. Another possible solution for those who can't move to 2008 yet, is to write a table-valued UDF that generates a table from a delimited string. Then you can do something like this:
SELECT DISTINCT
CN.user_id
FROM
dbo.Names N
INNER JOIN dbo.Connect_Names CN ON CN.name_id = N.name_id
INNER JOIN dbo.GetTableFromNameList(#names) T ON T.name = N.name
This will give you user IDs where ANY of the passed names match ANY of the user's names. If you want to change it so that it only gives a match when ALL of the passed names match one of the user's names then you could do something like this:
SELECT
CN.user_id
FROM
dbo.Names N
INNER JOIN dbo.Connect_Names CN ON CN.name_id = N.name_id
INNER JOIN dbo.GetTableFromNameList(#names) T ON T.name = N.name
GROUP BY CN.user_id
HAVING COUNT(*) = (SELECT COUNT(*) FROM dbo.GetTableFromNameList(#names))
I haven't tested that second query, so you may need to fiddle with it. Also, you might want to create a local table and fill it with the function so that you don't have to run the function multiple times.
I can post my own version of the string to table function if you need it. Also, if you want to use LIKE rather than exact matches, you can do that.
I hear that SQL Server 2008 has a feature called Table Parameters, so you can pass a Table as a parameter to a Function or Stored Procedure.
Until then, you could use the classic:
SELECT UserId FROM NameTable WHERE CHARINDEX( '|' + Name + '|', '|Filip Ekberg|Filip|Ekberg Filip|') > 0
This means that column name can be any of the values you have in the list.
You can also pass an XML parameter into the stored procedure and then use it as a table in your code, via the OPENXML command.
You say you want to use IN ? IN looks for exact matches so if you use IN('Ekberg')
You will only find Ekberg and not Anders Ekberg.
If you want the above to find Anders Ekberg you need Like '%Ekberg'
In fact in your second example where you have name A B C and you pass A C you want it to find A B C.
Really you want a Like on every word you pass. There are other ways to achieve what it sounds like you require. Mind if I ask the database you are running against first of all ? :) Thanks
From the similar SQL-only question here: SQL query: Simulating an "AND" over several rows instead of sub-querying
Try this.
List<string> targets = new List<string>() {"Johan", "Ekberg"};
int targetCount = targets.Count;
//
List<Users> result =
dc.Names
.Where(n => targets.Contains(n.Name))
.SelectMany(n =>
dc.ConnectNames.Where(cn => cn.NameId == n.NameId)
)
.GroupBy(cn => cn.UserId)
.Where(g => g.Count() == targetCount)
.SelectMany(g =>
dc.Users.Where(u => u.UserId == g.Key)
)
.ToList();
This is freehand code, so there's probably some syntax errors in there.

Categories