Entity Framework 4 search on combined fields - linq

How do I search on two combined fields. The search should happen on the SQL end if possible.
So say I have a customer table with first name and last name. I would like users to be able to search on both of those columns using a single search box.
My query currently looks like this:
var query = DbContext.Customers
.Where(c => c.FirstName.Contains(search) || c.LastName.Contains(search));
but it should be something like
var query = DbContext.Customers
.Where(c => c.FullName.Contains(search));

It is not possible unless you have FullName column also mapped. The way around this problem can be String.Concat which is allowed in Linq-to-entities:
var query = DbContext.Customers
.Where(p => String.Concat(p.FirstName, " ", p.LastName)
.Contains(search));

You could use a computed column in the database and map that
e.g.
alter table Customer add FullName AS FirstName + ' ' + LastName
(Not pretty I know)

Related

Select from multiple tables based upon search term but good in performance

I have a query in which I pass the search term to filter the list of Companies, either by Email or company Title which is stored in another table (TranslationTexts) as Text column for multiple locales.
The query runs fine but it is very heavy and takes time. How can I make it more efficient?
See Table Diagram Image
The query:
gm.ListData = context.Companies.ToList()
.Where(a => a.AspNetUser.Email.NullableContains(searchTerm) ||
a.TitleTranslation.TranslationTexts
.Where(b => b.Text.NullableContains(searchTerm)).Any()
).Select(c => new ListCompany
{
CompanyID = c.CompanyID,
EmailID = c.AspNetUser.Email,
Title = c.TitleTranslation.TranslationTexts.FirstOrDefault(d => d.Locale == Locale).Text
}).ToList();

Convert string value to entity in linq where query

I am using jqgrid in MVC 4. I have written a method for getting a list in linq.
In my function I am getting all values of jqgrid with search criteria i.e. Operator AND/OR
, operations Equals to, not equals to etc. Here I am also getting the column name like Name, City, State etc.
My problem is I can't pass the column name directly in linq query i.e. I have to use the column name as x => x.Name
switch (rule.field)
{
case "Name":
query = query.Where(x => x.Name.StartsWith(rule.data, StringComparison.OrdinalIgnoreCase));
break;
case "Course":
query = query.Where(x => x.Course.StartsWith(rule.data, StringComparison.OrdinalIgnoreCase));
break;
}
In rule.field I am getting column Name i.e. Name, city, state etc. I want to pass the column name which I am getting in rule.filed in LINQ query instead of x =>x.Name.
Is there any way to do it so I can avoid writing switch cases?
You can use System.Linq.Dynamic, which can be installed as a Nuget package along with string.Format. The syntax would then look something like..
var newquery = query.AsQueryable()
.Where(
string.Format("{0}.ToUpper().StartsWith(#0)", rule.field)
,rule.data.ToUpper());
You could always use reflection:
query = query.ToList().Where(p => {
var field = p.GetType().GetProperty(rule.field);
var value = (String) field.GetValue(p);
return value.StartsWith(rule.data, StringComparison.OrdinalIgnoreCase);
});
Warning: Reflection is slow. Only use this for short lists. Since you're dealing with UI rendering, I'm assuming this won't be a problem.
edit: My example is assuming all properties are indeed properties (and not fields), and that all properties are Strings. You may need to alter the code for your specific case.

Prioritizing fields when matching multiple fields with linq

I have a database with fields like firstname lastname street and searchfield. Anything that match the search field will be in my search subset here is the linq logic :
if (!String.IsNullOrEmpty(searchString))
{
folders = folders.Where(p => p.SearchField.ToLower().Contains(searchString.ToLower()));
}
I can order it by name or firstname or whatever.
Now I would like to present the results so it prioritize the name field in relation to my search term.
For example if i look for Schmid i want to show first the people with the LastName that match Schmid then the firstname then the street ...etc
Any idea ?
I hope I understood it correctly
var res =
folders
.Where(item => item.FirstName == name)
.Union(folders.Where(item => item.LastName == name))
/* Add more Union-Where statements */
;
I think the best approach is to get the matching objects first and then proceed in memory:
var lower = searchString.ToLower();
folders = folders
.Where(p => p.SearchField.ToLower().Contains(lower))
.ToArray();
folders = folders
.OrderBy(f => !f.LastName.Contains(lower))
.ThenBy(f => !f.FistName.Contains(lower))
.ThenBy(f => !f. ...
If you do all the OrderBy's on the IQueryable the query will probably blow up, while the initial filter is the most important thing to use the database engine for.
Note that you cannot always show the items that match lower in LastName and then those with a match in FistName etc., because there may be items that have a match in both. I don't think you want to duplicate items, do you?

LINQ Lamba Select all from table where field contains all elements in list

I need a Linq statement that will select all from a table where a field contains all elements in a list<String> while searching other fields for the entire string regardless of words.
It's basically just a inclusive word search on a field where all words need to be in the record and string search on other fields.
Ie I have a lookup screen that allows the user to search AccountID or Detail for the entire search string, or search clientID for words inclusive words, I'll expand this to the detail field if I can figure out the ClientId component.
The complexity is that the AccountId and Detail are being searched as well which Basically stops me from doing the foreach in the second example due to the "ors".
Example 1, this gives me an the following error when I do query.Count() afterwards:
query.Count(); 'query.Count()' threw an exception of type 'System.NotSupportedException' int {System.NotSupportedException}
+base {"Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator."} System.SystemException {System.NotSupportedException}
var StrList = searchStr.Split(' ').ToList();
query = query.Where(p => p.AccountID.Contains(searchStr) || StrList.All(x => p.clientID.Contains(x)) || p.Detail.Contains(searchStr));
Example 2, this gives me an any search word type result:
var StrList = searchStr.Split(' ').ToList();
foreach (var item in StrList)
query = query.Where(p => p.AccountID.Contains(searchStr) || p.clientID.Contains(item) || p.Detail.Contains(searchStr));
Update
I have a table with 3 fields, AccountID, ClientId, Details
Records
Id, AccountID, CLientId, Details
1, "123223", "bobo and co", "this client suxs"
2, "654355", "Jesses hair", "we like this client and stuff"
3, "456455", "Microsoft", "We love Mircosoft"
Search examples
searchStr = "232"
Returns Record 1;
searchStr = "bobo hair"
Returns no records
searchStr = "bobo and"
Returns Record 1
searchStr = "123 bobo and"
Returns returns nothing
The idea here is:
if the client enters a partial AccountId it returns stuff,
if the client wants to search for a ClientId they can type and cancel down clients by search terms, ie word list. due to the large number of clients the ClientID Will need to contain all words (in any order)
I know this seems strange but it's just a simple interface to find accounts in a powerful way.
I think there are 2 solutions to your problem.
One is to count the results in memory like this:
int count = query.ToList().Count();
The other one is to not use All in your query:
var query2 = query;
foreach (var item in StrList)
{
query2 = query2.Where(p => p.clientID.Contains(item));
}
var result = query2.Union(query.Where(p => p.AccountID.Contains(searchStr) || p.Detail.Contains(searchStr)));
The Union at the end acts like an OR between the 2 queries.

LINQTOSQL Help needed

I'm trying to add a column to the following LINQ expression. I want the column to contain a string concatenation of a text value in a many table called WasteItems. The join would be on "Waste.WasteId = WasteItem.WasteId". My problem is I need to display in a single dynamic column a string such as "EW (5); EX (3)" if there was 8 records in WasteItem and the column containing the 2 character string was called WasteItem.EWC. Hope that makes sense, there must be an efficient way since I realise LINQ is very powerfull. I'm new to it and not sure how to start or go about this:
return from waste in this._db.Wastes
where (from u in _db.UsersToSites.Where(p => p.UserId == userId && p.SystemTypeId == SystemType.W)
select u.SiteId)
.Contains(waste.SiteId)
orderby waste.Entered descending select waste;
THANKS IN ADVANCE
Something like this should do:
wastes.GroupJoin(db.WasteItems, w => w.WastId, wi => wi.WasteId, (w,wi) => new { w, wi })
.AsEnumerable()
.Select(x => new
{
x.w.Name,
Items = string.Join(", ", x.wi.GroupBy(wi => wi.EWC).Select(g => string.Format("{0} ({1})", g.Key, g.Count())))
})
Where wastes is the result from your query. The AsEnumerable() is necessary because Entity Framework can not handle string.Join, so that part must be dealt with in memory.
I could not check the syntax, obviously, but at least it may show you the way to go.

Resources