C# search query with linq - c#

I am trying to make a suitable linq query to accomodate my search functionality.
I have a table with the following columns: 'firstname' | 'lastname' | 'description'.
with the following data: 'Peter' | 'Mulder' | 'This is a little description.'
My 'search' keyword could be something like: "peter" or "a little description".
Now if I use the following linq expression in lambda:
mycontext.persons
.Where(t =>
search.Contains(t.Firstname) ||
search.Contains(t.Lastname) ||
search.Contains(t.Description).Select(p => p)
.ToList();
Now I get my result, when I use 'peter', but if I use 'pete' or 'a little description' I get no results.
How can I make my linq expression, so it can search through the column data for matches?

I think you just have it backwards:
mycontext.persons
.Where(t =>
t.Firstname.Contains(search) ||
t.Lastname.Contains(search) ||
t.Description.Contains(search))
.ToList();

One possible (but probably not the most optimized solution) would be to append all of your fields together and do a Contains on the search term., e.g.
var result = persons.Where(q => (q.Description + " " q.FirstName + " " q.LastName)
.ToLower()
.Contains(searchTerm.ToLower()))
.ToList();

try This Code.
private void SearchData()
{
Model1Container model = new Model1Container();
try
{
var query = model.Scholars.AsQueryable();
if (!string.IsNullOrEmpty(this.txtSearch.Text))
{
// query = query.Where(x=>x.ScholarName.StartsWith(txtSearch.Text));
query = (from Schl in model.Scholars
where Schl.ScholarName.StartsWith(txtSearch.Text) ||
Schl.PhoneRes.StartsWith(txtSearch.Text) ||
Schl.PhoneOff.StartsWith(txtSearch.Text) ||
Schl.Mobile.StartsWith(txtSearch.Text) ||
Schl.Email.StartsWith(txtSearch.Text)
orderby Schl.ScholarName
select Schl);
}
this.dgvScholarList.DataSource = query.ToList();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

Related

No property or field 'p' exists in type 'productByCategory'

I am trying to create a dynamic query for filtering my data results. I am building a linqToSql query containing subqueries and filtering the result after that using
System.Linq.Dynamic. But my Approach is ending up in errors and i cant find a good amount of documentations over the library samples.
here is what i am trying to do. i am using a list to filter data by given filter ids, after i fetch the result with added sub query:
List<long?> filter_list = new List<long?>();
string query = "";
if(mapped_filters!=null)
if (mapped_filters.Length > 0)
{
int i=0;
foreach (string item in mapped_filters)
{
long filter = 0;
long.TryParse(item, out filter);
filter_list.Add(filter);
query += " p.prod_attrs.Contains(#"+i+") ||";
i++;
}
}
query = query.TrimEnd(new char []{ '|','|'});
and following is the code for linq fetch on my products:
var productByCategory = db.products.Where(p => p.is_active == true && p.mainCat_id == cat_id).
Select(p => new productByCategory
{
id = p.id ,
prod_name = p.product_name,
prod_price = db.product_pricings.Where(pr => pr.prod_id == p.id && pr.currency_id == 2).FirstOrDefault(),
//filters below
prod_attrs = db.prod_attr_maps.Where(pa => pa.prod_id == p.id && pa.is_active == true
&& pa.currency_id == 2)
.Select(pa => pa.attr_id).ToList()
}).Where(p =>
(p.prod_price.our_price + p.prod_price.shipping_cost) >= min &&
(p.prod_price.our_price + p.prod_price.shipping_cost) <= max)
.Where("p=>"+query,filter_list)
.OrderBy(p => p.newItem).ToList();
but this ends up in error :
No property or field 'p' exists in type 'productByCategory'
..... Parse Exception
So far i have tried replacing
.Where("p=>"+query,filter_list)
To
.Where(""+query,filter_list)
and query part to
query += " prod_attrs.Contains(#"+i+") ||";
but still same error p replaced by prod_attrs exception.
EDIT
i did some direct string placement in the query and it seems like a problem with dynamic linq dll, when i do query like this :
.Where("id==1 && prod_name.Contains(\"a\")") it works fine
but with
.Where("id==1 && prod_name.Contains(1)")
it ends up in error :
An exception of type 'System.InvalidOperationException' occurred in
System.Core.dll but was not handled in user code
Additional information: No generic method 'Contains' on type
'System.Linq.Enumerable' is compatible with the supplied type
arguments and arguments. No type arguments should be provided if the
method is non-generic.
So it clearly seems to be a functionality issue with dynamic linq library.

How to get SecondOrDefault?

I have a simple linq lambda statement
Interactions = new BindableCollection<InteractionDTO>(ctx.Interactions.Where(x => x.ActivityDate > DateTime.Today)
.Select(x => new InteractionDTO
{
Id = x.Id,
ActivityDate = x.ActivityDate,
subject = x.Subject,
ClientNames = x.Attendees.Count == 1 ? x.Attendees.FirstOrDefault().Person.CorrespondenceName :
x.Attendees.FirstOrDefault().Person.CorrespondenceName : "Multiple attendees"
}));
This will give me the first Client Name, I'm trying to have it appear First 2 attendees followed by dots. I tried this
ClientNames = x.Attendees.Count == 1 ?
x.Attendees.FirstOrDefault().Person.CorrespondenceName :
x.Attendees.FirstOrDefault().Person.CorrespondenceName +
x.Attendees.Skip(1).FirstOrDefault().Person.CorrespondenceName + " ..."
But I get this error:
The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.
You could try ordering first, as the message suggests.
I'm not sure what your Attendees class looks like, but assuming it has an ID field:
x.Attendees.OrderBy(a => a.ID)
.Skip(1)
.Select(a => a.Person.CorrespondenceName).FirstOrDefault() + " ..."
A couple other notes:
I swapped your Select and FirstOrDefault statements. The way you've currently got it, if FirstOrDefault() returns null, then Person.CorrespondenceName will throw an exception.
But now if no record is found, you'll end up with just "...". You might want to adjust your first Where clause to filter out records that have no correspondance name, and then change FirstOrDefault() to First().
EDIT:
That should steer you in the right direction (I hope). This may be more what you're looking for, assuming it can actually be translated into a valid SQL statement:
ClientNames = x.Attendees.Any()
? x.Attendees.Count == 1
? x.Attendees.Select(a => a.Person.CorrespondenceName).FirstOrDefault()
: x.Attendees.Count == 2
? string.Join(", ", x.Attendees.OrderBy(a => a.ID).Take(2)
.Select(a => a.Person.CorrespondenceName).FirstOrDefault()
: string.Join(", ", x.Attendees.OrderBy(a => a.ID).Take(2)
.Select(a => a.Person.CorrespondenceName).FirstOrDefault() + " ..."
: "No client name available";
// put this in your namespace...
public static class EnumerableExtension
{
public static TSource SecondOrDefault<TSource>(this IEnumerable<TSource> source)
{
var iterator = source.GetEnumerator();
if (iterator.MoveNext() && iterator.MoveNext() )
return iterator.Current;
else
return default( TSource );
}
}
// Usage... var thing = list.SecondOrDefault();

Is it possible to use a string for a LINQ query expression?

I need to extract some records if some variables have some values.
For example, if status>0 I need to filter result like :
where object.id=status
else, if status=0, I need to remove this where clauses and return all elements. I'll get rid about :
if(status>0)
do a linq query with the where clauses
else
do a link query with that where clauses
too much code, because the variables to check could be more than 4-5.
Is it possible to "inject" a sort of string on LINQ? (So i can create my string and pass it to the LINQ).
I mean somethings like :
string myQuery="";
if(status>0)
myQuery="where object.id=status";
else
myQuery="";
is it possible? (Classic mysql behaviour).
Since LINQ is lazy, you can just do
var query = ...
if (status > 0)
{
query = query.Where(o => o.id == status);
}
You can build up a query like this:
IEnumerable<MyEntity> results = MyEntityContext.MyEntities;
if (status > 0)
results = results.Where(e => e.id == status);
Does that help?
It is possible using Dynamic LINQ, see ScottGu's blog post:
Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)
You could do it like this:
var query = database.MyTable.Where(/* where for all records */);
if (status > 0) {
query = query.Where(o => o.id == status);
}
Linq (to sql and EF) is smart enough to merge the where conditions and send one SQL statement to the database.
It is possible using dynamic linq - see How to create LINQ Query from string?
My answer there links to Scott Gu's posts and the sample code from Microsoft - http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
AFAIK, you will need to have 2 different queries.
if(status > 0)
{
var myquery = From ....
where object.id = status
}
else
{
var myquery = From ..
}
Another option: query.Where(x=>(status>0? x.id==status : 1==1))
Are you attempting to do a conditional LINQ query? if so maybe this would help
var nums = new List<int>() { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 };
bool getOdd = true;
var query = nums.AsQueryable();
if (getOdd) {
query = query.Where(i => i == 1 || i == 3);
} else {
query = query.Where(i => i == 2 || i == 4);
}
var result = query.ToList();
You can write the following
IQueryable<T> query = dbContext.SomeObjectSet;
if (condition1) {
query = query.Where(...)
}
if (condition2) {
query = query.Where(...)
}
However, you you want to query all entities, you can filter in memory afterwards using LINQ to SQL

How can I add variable count of SqlMethods.Like() in one query?

Can you help me with next: I'd like to add variable count of SqlMethods.Like() in one query?
For example: I've four words in list catCode, how can I create LINQ query with four SqlMethods.Like()?
using (var db = new MappingDataContext(_connection))
{
db.ObjectTrackingEnabled = false;
return (
from r in db.U_CDW_REPORTs
where (catCode.Length > 0 ?
SqlMethods.Like(r.CATEGORY, catCode) : r.CATEGORY != "*")
where r.SI_QTY > 0
orderby r.SI_QTY descending
select new Item
{
...
}).ToList();
}
What you need is dynamically OR-ing SqlMethod.Like operations. You need the PredicateBuilder.
Update: Here is an example of how to use the predicate builder.
string[] searchItems =
new string[] { "Pattern1", "Pattern2", "Pattern3" };
var likeExpression = PredicateBuilder.False<U_CDW_REPORT>();
foreach (string searchItem in searchItems)
{
var searchPattern = "%" + searchItem + "%";
likeExpression = likeExpression.Or(r =>
SqlMethods.Like(r.CATEGORY, searchPattern));
}
return (
from r in db.U_CDW_REPORTs.Where(likeExpression)
where r.SI_QTY > 0
orderby r.SI_QTY descending
select new Item { ... }).ToList();
You mean you have 4 disjoint LIKE clauses?
from entityRow in ctx.MyEntity
where (SqlMethods.Like(entityRow.Col, "%Pattern1%")
|| SqlMethods.Like(entityRow.Col, "%Patte%rn2%")
|| SqlMethods.Like(entityRow.Col, "Pattern3%")
|| SqlMethods.Like(entityRow.Col, "%Patte%rn4%"))
select entityRow;
UPDATE:
In that case, take a look at this: LINQ for LIKE queries of array elements
EDIT: made the code simpler. Also notice the updated explanation below.
You can build upon the query with multiple Where clauses in the following manner. Note that this approach ANDs the Where clauses, meaning all search terms will need to exist. In other words it's similar to ... where (condition1) && (condition2) && (conditionN).
string[] words = { "A", "B", "C" };
var query = dc.Products.AsQueryable(); // gives us an IQueryable<T> to build upon
foreach (var s in words)
{
query = query.Where(p => SqlMethods.Like(p.ProductName, "%" + s + "%"));
}
foreach (var item in query)
{
Console.WriteLine(item.ProductName);
}
The idea is to set the first part of the query up, then loop over the search terms and update the query. Of course you should update your pattern as needed; I used %word% for illustration purposes only.

How can I conditionally apply a Linq operator?

We're working on a Log Viewer. The use will have the option to filter by user, severity, etc. In the Sql days I'd add to the query string, but I want to do it with Linq. How can I conditionally add where-clauses?
if you want to only filter if certain criteria is passed, do something like this
var logs = from log in context.Logs
select log;
if (filterBySeverity)
logs = logs.Where(p => p.Severity == severity);
if (filterByUser)
logs = logs.Where(p => p.User == user);
Doing so this way will allow your Expression tree to be exactly what you want. That way the SQL created will be exactly what you need and nothing less.
If you need to filter base on a List / Array use the following:
public List<Data> GetData(List<string> Numbers, List<string> Letters)
{
if (Numbers == null)
Numbers = new List<string>();
if (Letters == null)
Letters = new List<string>();
var q = from d in database.table
where (Numbers.Count == 0 || Numbers.Contains(d.Number))
where (Letters.Count == 0 || Letters.Contains(d.Letter))
select new Data
{
Number = d.Number,
Letter = d.Letter,
};
return q.ToList();
}
I ended using an answer similar to Daren's, but with an IQueryable interface:
IQueryable<Log> matches = m_Locator.Logs;
// Users filter
if (usersFilter)
matches = matches.Where(l => l.UserName == comboBoxUsers.Text);
// Severity filter
if (severityFilter)
matches = matches.Where(l => l.Severity == comboBoxSeverity.Text);
Logs = (from log in matches
orderby log.EventTime descending
select log).ToList();
That builds up the query before hitting the database. The command won't run until .ToList() at the end.
I solved this with an extension method to allow LINQ to be conditionally enabled in the middle of a fluent expression. This removes the need to break up the expression with if statements.
.If() extension method:
public static IQueryable<TSource> If<TSource>(
this IQueryable<TSource> source,
bool condition,
Func<IQueryable<TSource>, IQueryable<TSource>> branch)
{
return condition ? branch(source) : source;
}
This allows you to do this:
return context.Logs
.If(filterBySeverity, q => q.Where(p => p.Severity == severity))
.If(filterByUser, q => q.Where(p => p.User == user))
.ToList();
Here's also an IEnumerable<T> version which will handle most other LINQ expressions:
public static IEnumerable<TSource> If<TSource>(
this IEnumerable<TSource> source,
bool condition,
Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
{
return condition ? branch(source) : source;
}
When it comes to conditional linq, I am very fond of the filters and pipes pattern.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/
Basically you create an extension method for each filter case that takes in the IQueryable and a parameter.
public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id)
{
return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query;
}
Doing this:
bool lastNameSearch = true/false; // depending if they want to search by last name,
having this in the where statement:
where (lastNameSearch && name.LastNameSearch == "smith")
means that when the final query is created, if lastNameSearch is false the query will completely omit any SQL for the last name search.
Another option would be to use something like the PredicateBuilder discussed here.
It allows you to write code like the following:
var newKids = Product.ContainsInDescription ("BlackBerry", "iPhone");
var classics = Product.ContainsInDescription ("Nokia", "Ericsson")
.And (Product.IsSelling());
var query = from p in Data.Products.Where (newKids.Or (classics))
select p;
Note that I've only got this to work with Linq 2 SQL. EntityFramework does not implement Expression.Invoke, which is required for this method to work. I have a question regarding this issue here.
It isn't the prettiest thing but you can use a lambda expression and pass your conditions optionally. In TSQL I do a lot of the following to make parameters optional:
WHERE Field = #FieldVar OR #FieldVar IS NULL
You could duplicate the same style with a the following lambda (an example of checking authentication):
MyDataContext db = new MyDataContext();
void RunQuery(string param1, string param2, int? param3){
Func checkUser = user =>
((param1.Length > 0)? user.Param1 == param1 : 1 == 1) &&
((param2.Length > 0)? user.Param2 == param2 : 1 == 1) &&
((param3 != null)? user.Param3 == param3 : 1 == 1);
User foundUser = db.Users.SingleOrDefault(checkUser);
}
I had a similar requirement recently and eventually found this in he MSDN.
CSharp Samples for Visual Studio 2008
The classes included in the DynamicQuery sample of the download allow you to create dynamic queries at runtime in the following format:
var query =
db.Customers.
Where("City = #0 and Orders.Count >= #1", "London", 10).
OrderBy("CompanyName").
Select("new(CompanyName as Name, Phone)");
Using this you can build a query string dynamically at runtime and pass it into the Where() method:
string dynamicQueryString = "City = \"London\" and Order.Count >= 10";
var q = from c in db.Customers.Where(queryString, null)
orderby c.CompanyName
select c;
You can create and use this extension method
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
return isToExecute ? source.Where(predicate) : source;
}
Just use C#'s && operator:
var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")
Edit: Ah, need to read more carefully. You wanted to know how to conditionally add additional clauses. In that case, I have no idea. :) What I'd probably do is just prepare several queries, and execute the right one, depending on what I ended up needing.
You could use an external method:
var results =
from rec in GetSomeRecs()
where ConditionalCheck(rec)
select rec;
...
bool ConditionalCheck( typeofRec input ) {
...
}
This would work, but can't be broken down into expression trees, which means Linq to SQL would run the check code against every record.
Alternatively:
var results =
from rec in GetSomeRecs()
where
(!filterBySeverity || rec.Severity == severity) &&
(!filterByUser|| rec.User == user)
select rec;
That might work in expression trees, meaning Linq to SQL would be optimised.
Well, what I thought was you could put the filter conditions into a generic list of Predicates:
var list = new List<string> { "me", "you", "meyou", "mow" };
var predicates = new List<Predicate<string>>();
predicates.Add(i => i.Contains("me"));
predicates.Add(i => i.EndsWith("w"));
var results = new List<string>();
foreach (var p in predicates)
results.AddRange(from i in list where p.Invoke(i) select i);
That results in a list containing "me", "meyou", and "mow".
You could optimize that by doing the foreach with the predicates in a totally different function that ORs all the predicates.

Categories