LINQ: Multiple LIKE clauses in a single predicate - c#

I'd like to do a LINQ query that can compare multiple variables to a single string. I've seen LINQ for LIKE queries of array elements, and it's helpful, but not quite. I need the reverse.
What I'd like to do is the following: let's say I have a Company object with both Name and Address. I also have a string keyword. Then, I'd like to find all Companys in a list that have the keyword in either their Name or Address. In SQL it would be...
SELECT * FROM Company
WHERE Name LIKE '%keyword%' OR Address LIKE '%keyword%'
I've been using Entity Framework, and I've tried the following: context.Companies.Where(x => new string[] { x.Name, x.Address }.Contains(keyword), as well as context.Companies.Where(x => new string[] { x.Name, x.Address }.Any(r => r.Contains(keyword)), but neither were successful. The first one gives me an IN clause, and the second one... I don't know what it does, but it doesn't give me what I want.
I'm sorry I don't have a very in-depth understanding of Expressions (yet); I wished I was able to write my own custom Expressions from scratch, but scratch it I can't just yet... Can anybody help me with this?

Any reason for not just using the || operator?
context.Companies.Where(x => x.Name.Contains(keyword) ||
x.Address.Contains(keyword))
I'd expect this to be translated into your original SQL.

Related

Search and select multiples with where linq/lambda expressions

I currently have the following code:
var FirstNameList = db.Clients.Include(x => x.FirstNames).Include(x => x.Addresses).SelectMany(a => a.FirstNames).Where(x => x.Name.ToLower().Trim() == "Max".ToLower().Trim()).ToList();
I have a navigation property of FirstNames and Addresses which I which to include in the result.
I use the SelectMany Statement because it, for me, is the only one which works. Kind of. It returns all the FirstNames where the Name equals Max.
What I would like it to do is return all the Clients who have the property Name equals Max from the table FirstNames.
the other way I thought about doing this was to take all the ID's returned from FirstNameList and then returning the Clients by querying the data against the FirstNameList but I would be then querying the database twice, which seems inefficient.
My question is is it possible, and how would I go about querying the database to return my Clients, if it was?
Kind regards
The following query should give you what you're looking for. You can look within each client's FirstNames and see if any of them are named "max". (In this case, since "max" is a constant you're typing in, I removed the ToLower().Trim() from it)
var clientsNamedMax = db.Clients.Include(x => x.FirstNames).Include(x => x.Addresses).Where(x => x.FirstNames.Any(y => y.Name.ToLower().Trim() == "max")).ToList();

Searching Multiple Fields with LINQ Contains or Other

I'm using LINQ to search multiple fields on a single phrase and I'm using Contains() to do this. Up until today, when I noticed that the find method isn't working correctly.
So I'd like to be able to search each of the fields where there is any match. I've done some searching on Google and through SO and found suggestions of using Any()? Does anyone have any suggestions?
var l = _getData().Select(_marshallData).ToList();
return l.Where(x => x.Name.Contains(what) || x.Pumpkin.Contains(what) || x.Orange.Contains(what) || x.Tomato.Contains(what)).ToList();
Please excuse the stupid field names, I've had to change them for confidentiality.
Since you're materializing the data already you could do this:
var l = _getData().Select(_marshallData).AsEnumerable();
return l.Where(x => new[] { x.Name, x.Pumpkin, x.Orange, x.Tomato }.Any(s => s.Contains(what)));
But if you're using an ORM (like Entity Framework) this trick probably won't work (since there's a good chance this can't be converted to a SQL expression). In this case, you're original solution is fine, but it would probably be better to do this before you materialize the data.
return _getData()
.Where(x =>
x.Name.Contains(what) || x.Pumpkin.Contains(what) ||
x.Orange.Contains(what) || x.Tomato.Contains(what))
.Select(_marshallData)
.AsEnumerable();
Of course the field names here might be different, since the parameter x is probably a different type. Without further information, it's up to you to write this filter correctly.
Note that in both examples, I've eliminated the calls to ToList. Unless you absolutely need the result to a List<T> this is probably unnecessary.

Linq To SQL: Sort Query by Arbitrary Property(Column) Name

I have a larger/more complex problem, but for simplicity sake, let us consider the following:
Let us say that I have table in the SQL DataBase called Product, having two columns, ID (int, primary key) and Name (varchar/string). I also have a simple LINQ DataContext.
I have a query constructed and handed to “my” function. Let us say it is something like: (though it may be a bit more complex)
IQueryable<Product> query = from p in db.Products select p;
Once my method gets this query, passed in as a parameter, it has to change the sort order e.g.
IQueryable<Product> sortedQuery = query.OrderBy(x => x.Name);
I would like to make this more generic i.e. to specify the field to sort on. Normally, I can do a switch statement that takes a string. However I would like to know if there is a way to pass the parameter directly. I intend to extend this to other Database tables, so these switch statements would get tedious.
I was trying something like:
IQueryable<Product> sortedQuery = query.OrderBy(x => (typeof(Product)).GetProperty(“Name”));
But this does not work. I also want to ensure that the LINQ to SQL is maintained i.e. the sort to be done on the SQL Server. Hence if I debug, I should get a SQL query from this LINQ query.
Thank you in advance for your help.
You could use Dynamic Linq for this purpose.
See here Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)
Then you can make calls like this:
var query = DBContext.Users.Where( "Age > 3" ).OrderBy( "Name asc" );
Try this out instead:
query.OrderBy(x => x.GetType().GetProperty(“Name”).GetValue(x, null));
You can't just grab the property. You need to grab the value off of that property, hence the call to GetValue.
It is not as easy as it seems. The LINQ to SQL engine parses the expression you pass to the OrderBy method in order to obtain the name of the property you are referencing, then uses this information to compose a plain SQL order by clause.
I guess that maybe it can be done by using reflection, anyway. Maybe you can get something useful from the accepted answer of this SO question.

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;

c# Fluent SQL Helper - Syntax improvement

I'm writing a little library to help building SQL requests (only doing SELECTs for the moment) but I'm not satisfied with an aspect of the syntax, here's an exemple to explain:
var db = FluentDb.WithConnectionString("SqlCeTest");
var query = db.From("Customers")
.Where(FS.Col("Age") > 18 & FS.Col("Name").StartsWith("L"))
.OrderBy("BirthDate")
.Select("Name", "Age", "BirthDate");
var customers = query.ToList((r) => new
{
Name = r.Get<string>("Name"),
Age = r.Get<int>("Age"),
BirthDate = r.Get<DateTime?>("BirtDate")
});
The part I'd like to improve is the FS.Col("ColumnName"), it's supposed to stand for FluentSql.Column (return a new FluentColumn(columnName)), but I find it a bit long in that context, what I'd really like is to be able to use just Col("ColumnName")...
Do anybody see a trick I could use to achieve that, or another syntax idea?
My ideas:
Extension method on string: Where("Name".Col() == "Jon")
Lambda expression with factory object using indexer: .Where(c => c["Name"] == "Jon")
Anyone see something better/shorter/nicer?
Edit:
my second idea looks good but there's a downside if i use it in another context:
I sometime need to use FluentColumns in Select (or OrderBy, or GroupBy) statements like that:
query.Select(FS.Col("Name").As("Customer"), FS.Col("OrderId").Count().As("OrdersCount"));
I would have to repeat the 'c => ' for each column...
A twist on your second option (which is pretty good) would be to use a dynamic expandoobject in the lambda instead of a string indexer.
http://blogs.msdn.com/b/csharpfaq/archive/2009/10/01/dynamic-in-c-4-0-introducing-the-expandoobject.aspx
Just for information, I decided to go with an indexer syntax on the FluentDb instance:
db["Customer", "AddressId"] mean column AddressId of table Customer,
an alternative syntax is available: db["Customer"]["AddressId"]
So in the end, it's gonna be (I still need to find a trick to make the Column declaration without table nice):
.Where(db["Customer", "Name"] == "Jon")

Categories