I am trying to figure out a way to search through a User's contact details.
User can have more than one Contact
Here is my code:
_usersRep.Find(r=>(string.IsNullOrEmpty(vm.PhoneNumber)
|| r.UserContacts
.Select(o => o.TelephoneNo)
.Contains(vm.PhoneNumber))
This should bring back me the user that any of its contact contains the PhoneNumber that I pass it to it. I actually tried to find where it should be equal but I could not get it worked.
I have checked on the database that this user has a contact object and that contac object phone number is the one that I search for but it does not bring me the user.
The relationship between User and UserContacts is one to many.
I also would like to ignore the search filter if the text is empty? But when I do not type anything it brings me all users.
I think you are looking for something like:
_usersRep.Where(r => r.UserContacts.Any(uc => uc.TelephoneNo == vm.PhoneNumber))
So restricting which users are returned (Where clause) based on whether the user has Any usercontacts with a matching number.
EDIT multiple conditions:
_usersRep.Where(r => r.UserContacts.Any(uc => uc.TelephoneNo == vm.PhoneNumber &&
uc.EmailAddress == vm.EmailAddress)) // etc
So the logic is, for each UserContact we test, if ALL the conditions are true in the lambda expression, then it passes the predicate, in which case as we are using the Any test, the userRep is selected. Hope this makes sense!
OK so if some of the fields can legitimately be empty, we should make sure the filter is defined if we do a compare, so combining in some nested ORs works here:
_usersRep.Where(r => r.UserContacts.Any(uc =>
(String.IsNullOrEmpty(vm.PhoneNumber) || uc.TelephoneNo == vm.PhoneNumber) &&
(String.IsNullOrEmpty(vm.EmailAddress) || uc.EmailAddress == vm.EmailAddress)))
Note that if all fields in vm are blank, this will bring back everything, as there is no restriction. If you don't want this, you should probably check before calling this query.
Explanation
Just to make it clear how this works. If we take the basic clause for each field:
(String.IsNullOrEmpty(vm.PhoneNumber) || uc.TelephoneNo == vm.PhoneNumber)
We are saying if part A or B is true, then for this field we have a match. Note that the || operator evaluates A first, and only evaluates B if A is false. So, in plain English, if String.IsNullOrEmpty(vm.PhoneNumber) is true (ie if the user doesn't enter query text for this field) then it is a match, no matter what the PhoneNumbers actually are. So therefore we only test uc.TelephoneNo == vm.PhoneNumber when something was specified for vm.PhoneNumber
So the above determines that for a single field we have a match (either because the user didn't enter a query for this field, or because they did, and the strings match). Now we use the && to ensure that every field we are checking is a match based on the above test.
Now because we do String.IsNullOrEmpty first for every field in our filter, if this is true for every field, based on the above logic, everything is a match. This makes semantic sense as well - if you don't enter any terms to restrict the search, you would expect to receive everything.
If the above is a problem in your case, as I said before I would check this before you execute the query, and tell the user that they must enter at least one search term to restrict the search.
The reason why you are getting all of the users when the search string is because of this
_usersRep.Find(r=>(string.IsNullOrEmpty(vm.PhoneNumber) || ... )
If the phone number string is null or empty then the find will always return true, giving you all of the users
you want
if(!string.IsNullOrEmpty(vm.PhoneNumber))
_usersRep.Find(r=> ... )
For the other problem, try trimming your string
if(!string.IsNullOrEmpty(vm.PhoneNumber))
_usersRep.Find(r=>r.UserContacts
.Select(o => o.TelephoneNo)
.Contains(vm.PhoneNumber.Trim()))
Besides those changes don't notice anything else that should be giving you problems.
Assume that vm.PhoneNumber is your search filter, add ! to String.IsNullOrEmpty check and change || to &&
_usersRep.Find(r=>(!string.IsNullOrEmpty(vm.PhoneNumber)
&& r.UserContacts
.Select(o => o.TelephoneNo)
.Contains(vm.PhoneNumber))
Related
Net application. I have one entity with child entities. I have to filter based on child entity values. For example in the below query,
var sourceProposal = proposals.ProposalResults.Where(x => x.Quotes.All(c => c.QuotationId.ToLower().Trim() == sourceQuoteId.ToLower().Trim()));
I have input parameter sourceQuoteId which is present in child table quotes. Here parent table is Proposal. So there will be multiple proposal and each proposal has multiple quotes. Idea is to filter proposal based on the quote id. Above query works fine whenever only one quote exists but it will not filter when there are multiple quotes. Can someone help me to filter based on child table?
It seems that you should be calling Any rather than All, if what you want is proposals with any quote that satisfies the criterion.
The reason you are getting this working only when 1 result is in the collection is because you are using an All operator, All operator is used to check whether all elements in a sequence satisfy given condition or not.
What you want to use is the Any operator, Any operator is used to check whether any single element in a sequence satisfy a given condition or not.
Your query should then be:
var sourceProposal = proposals.ProposalResults
.Where(x => x.Quotes.Any(c => c.QuotationId.ToLower().Trim() == sourceQuoteId.ToLower().Trim()));
try this:
var sourceProposal = proposals.ProposalResults.Where(x => x.Quotes.Any(c => c.QuotationId.ToLower().Trim() == sourceQuoteId.ToLower().Trim()));
I am trying to implement a search function, but am running into problems when some of the fields are not filled in by a user.
string country = searchCountry.Text.ToLower();
string state = searchState.Text.ToLower();
var searchLocation= (from h in db.Locations where (!string.IsNullOrWhiteSpace(country) ? h.Country.ToLower().Contains(country):false)
&& (!string.IsNullOrWhiteSpace(state) ? h.State.ToLower().Contains(state) : false)
select h);
The problem is that when one of the strings is empty the searchLocation returns nothing and only works when both fields are filled in. I Have tried replacing the && with || but then it will get results, even if one of the search terms is not in the db.
Is there a way to do this, besides Filtering out null values in a linq search
This will return any locations where either the country is empty or it matches, and either the state is empty or it matches.
var searchLocation= (from h in db.Locations
where (string.IsNullOrWhiteSpace(country) || h.Country.ToLower().Contains(country))
&& (string.IsNullOrWhiteSpace(state) || h.State.ToLower().Contains(state))
select h);
It would help to have a bit more description of what you'd like to put in and get out, but this seems logical to me.
Either field would be optional, but it would filter results to include anything that matched all (one or two) filled in fields.
Of course, if you run this without any filters, it will return all locations. So keep that in mind if you're making requests to the database. If that's desired behavior, it might make sense to just pull all your data into a list beforehand, rather than querying every time anything is typed.
I believe you're overthinking this. Just validate the fields before searching:
string country = searchCountry.Text.ToLower();
string state = searchState.Text.ToLower();
if(string.IsNullOrWhitespace(state) || string.IsNullOrWhitespace(country))
{
//MessageBox.Show...
return;
}
var searchLocation= //query with validated fields
It's a very good idea to validate your input before trying to perform actions against it. And it makes your code more readable than combining the two.
I have an asp.net-mvc website that uses Fluent Nhibernate Linq / SQL Server. I have a textbox where someone can enter a name and that generate the following query that I am using now to search my Person table:
return Session.Query<Person>()
.Where(r => (r.LastName.Contains(s) || r.FirstName.Contains(s)));
This works as expected in terms of translating to a "SQL like query"
Select * from Person where FirstName like '%%' or LastName like '%%'
but I have 2 new requirements that I am not sure that nhibernate linq supports.
In some cases people are entering the name in upper or lower case so I want to be able to do a case insensitive search.
Since it's a single textbox, in some cases people type in both the first and last name (something like "Smith, Joe" and that fails to find a result given that overall string doesn't exist in either the first or last name fields. Besides breaking the UI up into separate fields (which I can't do for some other reasons) is there any suggestion on how I could support the query to include the following combination of user's search string
[First] [Last]
[Last], [First]
in the above search code.
To solve issue with mixed upper/lower, we can just convert both sides into .ToLower()
return Session.Query<Person>()
.Where(r => (r.LastName.ToLower().Contains(s.ToLower())
|| r.FirstName.ToLower().Contains(s.ToLower())));
Check this link for more details how the NHibernate native InsensitiveLikeExpression.cs is working (for almost every dialect it is doing the same) :
NHibernate IsInsensitiveLike treats strings as case sensitive
The second part, here is some super simple algorithm (if it is one at all)
var first = searched.Split(' ')[0].ToLower();
var last = searched.Split(' ')[1].ToLower();
var emps = session.Query<Person>()
.Where(e =>
(e.FirstName.ToLower().StartsWith(first)
&& e.LastName.ToLower().StartsWith(last))
||
(e.FirstName.ToLower().StartsWith(last)
&& e.LastName.ToLower().StartsWith(first))
)
Very similar solution could be used (and I do) for combobox searching... Where "Ra Ko" will also find Radim Kohler...
Your first point-
1). In some cases people are entering the name in upper or lower case
so I want to be able to do a case insensitive search.
Answer is already given by Radim Köhler - that convert both sides into .ToLower()
Now for your second point, below might be useful.
return session.Query<Person>()
.Where(e =>
((e.FirstName.ToLower() + " " + e.LastName.ToLower()).Contains(s))
||
((e.LastName.ToLower() + ", " + e.FirstName.ToLower()).Contains(s))
)
There might be any syntax error as i have not tested in VS.
I have a situation wherein two urls need to be compared, one coming in to the method and the other in the db. The one in the db may or may not be Url decoded and will have %20 for space and so on.
Here's the linq query in the method:
var Result = (from s in context.301Redirects
where s.IsDeleted == false && s.Status == true && HttpUtility.UrlDecode(s.OldURL).ToUpper() == HttpUtility.UrlDecode(OldURLPath).ToUpper()
select s.NewURL).FirstOrDefault();
return Result;
Inside the method I can UrlDecode the incoming param but what about the one in the db?
This one fails because EF will not recognize UrlDecode method and throw and exception saying so.
Is there a workaround?
You can bring the collection in memory, then perform the UrlDecode at that point by evaluating the query. Try:
var Result = (from s in context.301Redirects
where s.IsDeleted == false && s.Status == true)
.AsEnumerable()
.FirstOrDefault(HttpUtility.UrlDecode(s.OldURL).ToUpper() == HttpUtility.UrlDecode(OldURLPath).ToUpper();
return Result;
If your database table is huge though, I'd consider creating some sort of TRIGGER/update script to either URL encode or decode all records, that way you don't have to check at all, you just encode/decode the parameter passed in.
Do your adjustments before the query so that you have absolute values for comparisons in the query that align with the particular formats, and always check against the ones in the database, rather than transform them - so, this potentially includes using OR in your query, instead of pulling the whole thing into memory a la ToList.
I need to provide a null where clause that has no effect.
Currently I have:
f=>{f!=null;}
However that doesn't really look right. If I were to Select clients, I use
.Select(clients => clients)
With my filter I also get a warning about not all code paths returning a result.
Just return true:
foo.Where(f => true)
Your lambda expression doesn't work for three reasons:
You're trying to use f != null as a statement, which it isn't.
You don't have a return value.
It would reject null values.
The first two can be fixed by removing the braces:
foo.Where(f => f != null)
The last point means it's not really a no-op filter, which is what I guess you meant by "identity filter". Whether it's what you really want or not though, I can't say.