Linq : How to search with 'Any' or 'All' - asp.net-mvc-3

I have a search page where you can enter 4 search criteria. User can enter all 4 or less and when I do the search I have to look for all 4 if they are entered and if not take all for the value not entered. Example: my search parameters are FirstName, LastName, City and Zip. Now if someone enters John and Zip 01803 I need to ignore other 2 and look for all Johns in that zip code. Can I do that in one query somehow? I know I can do that if I write multiple ones to check which values are null but that looks not so tedious. I want to know if I can use 'Any' or 'All' in LINQ to do that?
if (fname == null) fname = "";
if (lname == null) lname = "";
if (city == null) city = "";
var results = from c in context.Company.Where(d => d.CompanyCode == identifikator)
join p in context.Person.Where(d => (d.FirstName.Any(??) || d.FirstName.Contains(fname)) && d.LastName.Contains(lname) && d.City.Contains(city) && d.ZIP==zip) on c.CompanyId equals p.Identifikation
join ts in context.TSSession on p.PersonId equals ts.PersonId
select new
{
firstname = p.FirstName,
lastname = p.LastName,
mail = p.EMail,
order = tp.Order,
timestamp = ts.TimeStamp,
personid = p.PersonId
};

You can build your where expression like this:
var personsSubQuery = context.Persons;
if(fname != null)
personsSubQuery = personsSubQuery.Where(x=>x.FirstName.Contains(fname));
if(lname != null)
personsSubQuery = personsSubQuery.Where(x=>x.LastName.Contains(lname));
and so on.
then in your query just use personsSubQuery in place of your context.Persons.Where.
I don't see Any or All methods to be usable in this scenario.

Related

LINQ EF AND VS2017

I wrote a query and worked on LINQPAD
from x in FacilityData
from y in FavInformation
where y.UserID == 1 && x.ID == y.FacilityID
select new
{
xID = x.ID,
xDistrictName = (from y in _Ilcelers
where y.ID == x.DistrictID
select y.IlceAd).FirstOrDefault(),
xName = x.Name,
Value = (from o in Tags
from p in Table_tags
where o.Prefix != null && o.Prefix == p._NAME && o.Facility == y.FacilityID
orderby p.İd descending
select new
{
FType = o.TagType,
Name = o.TagsName,
Value = p._VALUE,
Time = p._TIMESTAMP
}).Take(Tags.Count(h => h.Facility == y.FacilityID))
}
result
the result is perfect
but does not work in visual studio,
Value = (from o in DB.Tags
from p in DB.table_tags
where o.Prefix != null && o.Prefix == p.C_NAME && o.Facility == 11
orderby p.id descending
select new
{
FType=o.TagType,
Name = o.TagsName,
Value = p.C_VALUE,
Time = p.C_TIMESTAMP
}).Take(Tags.Count(h => h.Facility == y.FacilityID))
and it gives an error.
I guess the part with .Take() doesn't work because it's linq to EF.
error:
Limit must be a DbConstantExpression or a Db Parameter Reference Expression. Parametre name: count]
error image
thank you have a good day
Not sure but I will just throw it in. If you are talking about linq to ef/sql, it is possible they dont know a thing about C#. If take() would be the problem try to get the select result local first by doing .tolist(). Afterwards use your take funtion.
.ToList().Take(Tags.Count(h => h.Facility == y.FacilityID))

Using case statement in linq query

I have a linq query like this :
var trfplanList = (from at in entities.tdp_ProviderAccomodationType
join ap in entities.tdp_ProviderAccomodationTariffPlan on at.PATID equals ap.FK_PATID
join ac in entities.tdp_ProviderAccomodationCategory on ap.FK_PACID equals ac.PACID
where at.FK_ProviderID == CityID && at.IsApproved == 0 && ap.IsApproved == 0 && ac.AccomodationCategory == "Double Occupy"
orderby at.AccomodationType,ap.FromDate,ap.SType
select new AccomodationTariff
{
AccomodationType = at.AccomodationType,
SType = ap.SType,
FromDate = Convert.ToDateTime(ap.FromDate),
ToDate = Convert.ToDateTime(ap.ToDate),
RoomTariff = Convert.ToDecimal(ap.Rate),
ExPAXRate = Convert.ToDecimal(at.PerPaxRate)
}).ToList();
I have two questions:
Can't I convert value while assigning in the select new {} block ? it is giving me an error in project.
I want use 'case' while selecting ExPAXRate from the database for example in SQL I used to write :
CASE ap.SType WHEN 'Off Season' THEN at.PerPaxRateOS ELSE at.PerPaxRate END AS ExPAXRate
Can I use something like this in linq query ?
Can't I convert value while assigning in the select new {} block
No, you can't (sadly). EF doesn't know how to translate it into SQL.
I want use 'case'
You can use the ternary operator (?):
ExPAXRate = at.OffSeason ? at.PerPaxRateOS : at.PerPaxRate
(assuming that at.OffSeason exists).
A solution for the conversion issue could be to project into an anonymous type first and then, in memory, to AccomodationTariff:
...
select new
{
AccomodationType = at.AccomodationType,
SType = ap.SType,
FromDate = ap.FromDate,
ToDate = ap.ToDate,
RoomTariff = ap.Rate,
ExPAXRate = at.PerPaxRate
}).AsEnumerable()
.Select(x => new AccomodationTariff
{
AccomodationType = x.AccomodationType,
SType = x.SType,
FromDate = Convert.ToDateTime(x.FromDate),
ToDate = Convert.ToDateTime(x.ToDate),
RoomTariff = Convert.ToDecimal(x.Rate),
ExPAXRate = Convert.ToDecimal(x.PerPaxRate)
}).ToList();

Joining to a GROUP'ed query

I'm a LINQ Newb and I've got this query, which returns the pages in a survey. (These values are not materialized into a table, for whatever reason.)
//Group all of this data by page
var pages = from fq in db.FormQuestions
where (fq.FormId == id) && fq.Disabled == false
group fq by fq.PageNumber into p
select new DTOs.PageDTO { PageNumber = p.Key.Value };
And then I have this query, which projects all of the leaf-data into DTOs.
var questions = from fq in db.FormQuestions
join q in db.Questions on fq.QuestionId equals q.QuestionId
where (fq.FormId == id) && fq.Disabled == false
//where (fq.FormId == id) && fq.Disabled == false && fq.PageNumber == page
orderby fq.DisplayOrder
select new DTOs.FormQuestionDTO()
{
DisplayOrder = (fq.DisplayOrder.HasValue ? fq.DisplayOrder.Value : 0),
PageNumber = (fq.PageNumber.HasValue ? fq.PageNumber.Value : 0),
QuestionId = q.QuestionId,
QuestionSelectionMode = q.vts_tbQuestionSelectionMode.Description,
QuestionText = q.QuestionText,
Answers =
from answer in q.Answers
join at in db.AnswerTypes on answer.AnswerTypeId equals at.AnswerTypeID
where answer.Disabled == false
orderby answer.DisplayOrder
select new DTOs.AnswerDTO()
{
AnswerId = answer.AnswerId,
AnswerText = answer.AnswerText,
DisplayOrder = answer.DisplayOrder,
AnswerType = at.Description
}
};
Is there a way to join these two neatly? I.e., under each page DTO I want to see the QUestion DTOs, then inside of that Answer DTOs, and onward...
Also, even if I could do this all in one LINQ statement, is it preferable to build the LINQ statements separately and then merge them? This feels a bit like building temp variables in SQL in that it may be possible to build one giant query but it's a pain to maintain.
So why wouldn't something like this work?
//Group all of this data by page
var pages = from fq in db.FormQuestions
where (fq.FormId == id) && fq.Disabled == false
group fq by fq.PageNumber into p
select new DTOs.PageDTO {
PageNumber = p.Key.Value
questions = from fq in db.FormQuestions
join q in db.Questions on fq.QuestionId equals q.QuestionId
where (fq.FormId == id) && fq.Disabled == false
orderby fq.DisplayOrder
select new DTOs.FormQuestionDTO()
{
DisplayOrder = (fq.DisplayOrder.HasValue ? fq.DisplayOrder.Value : 0),
// etc as you 2nd code sample

In Linq2SQL, how do I get a record plus the previous and next in the sequence in a single query?

Given a date, what is the most efficient way to query the last record before that date, any record that equals that date, and the next one after that date.
It should be functionally equivalent to a query like this:
from asset in Assets
where asset.Id == assetId
select new {
Previous = (from a in a.Orders where a.Date < myDate orderby a.Date descending select a).FirstOrDefault(),
Current = (from a in a.Orders where a.Date == myDate select a).SingleOrDefault(),
Next = (from a in a.Orders where a.Date > myDate orderby a.Date select a).FirstOrDefault()
}
As is, this query runs three queries, and presumably has to sort the dataset by myDate three times to do it.
Some similar questions:
How do I get 5 records before AND after a record with a specific ID? (just uses two queries)
How do I get records before and after given one? Not in Linq, and therefore hard for me to take advantage of (my team will get annoyed).
To provide the "most efficient" query depends on what you mean by efficient.
If you want a single query to the database, a single sort of orders by date and finally fast look-ups by date then I suggest the following might be the most efficient. :-)
var orders =
(from a in Assets
where a.Id == assetId
from o in a.Orders
orderby o.Date
select o).ToArray();
var previous = orders.LastOrDefault(o => o.Date < myDate);
var current = orders.SingleOrDefault(o => o.Date == myDate);
var next = orders.FirstOrDefault(o => o.Date > myDate);
This should query the database once for the orders associated with the required asset Id, sort them by date, and return them as an array in memory. Since this is in memory it is now blindingly fast to look for the current, previous & next records for the specified date.
Does your Orders table have a sequential ID field? If so, you might be able to do it with:
from asset in Assets
where asset.Id == assetID
let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
where current != null
let previous = asset.Orders.Where(x => x.id == current.id - 1).FirstOrDefault()
let next = asset.Orders.Where(x => x.id == current.id + 1).FirstOrDefault()
select new {
Previous = previous,
Current = current,
Next = next
};
If it doesn't, then it'd be a bit more code:
from asset in Assets
where asset.Id == assetID
let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
where current != null
let previous = asset.Orders.Where(x => x.Date < current.Date).OrderByDescending(x => x.Date).FirstOrDefault()
let next = asset.Orders.Where(x => x.Date > current.Date).OrderBy(x => x.Date).FirstOrDefault()
select new {
Previous = previous,
Current = current,
Next = next
};
That should get compiled into a single SQL query that utilizes sub-queries. IE: the database server will execute multiple queries, but your client program is only submitting one.
Edit One other idea that would work if your Order table had sequential IDs:
var sample = (from asset in Assets
where asset.Id == assetID
let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
where current != null
from order in asset.Orders
where order.Id == current.id - 1
select order)
.Take(3)
.ToArray();
var Previous = sample[0];
var Current = sample[1];
var Next = sample[2];
Other Answers, for example, SkipWhile etc. very very slow. Good luck ^^
//Current Record
var query
= (from item in db.Employee
where item.UserName.Equals(_username)
select item).SingleOrDefault();
//Next Record
var query
= (from item in db.Employee
where item.UserName.CompareTo(_username) > 0
select item).FirstOrDefault();
//Previous Record
var query
= (from item in db.Employee
where item.UserName.CompareTo(_username) < 0
orderby item.UserName Descending
select item).FirstOrDefault();
Almost the same, but the SQL query plan might be different.
var q =
from asset in Assets
where asset.Id == assetID
select new
{
Previous = asset.Orders.where(a => a.Date == asset.Orders.Where(x => x.Date < myDate).Max(x => x.Date)).FirstOrDefault(),
Current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault(),
Next = asset.Orders.where(a => a.Date == asset.Orders.Where(x => x.Date > myDate).Min(x => x.Date)).FirstOrDefault()
};

Conditional Multiple Fields Searching and Filtering in LINQ

Assuming that we have the following table:
Person:
PersonID,
Name,
Age,
Gender
And we are providing a search function that allows users to search the table according to the name and/or the age.
The tricky part in writing the SQL ( or LINQ) query is that the users can choose to search for both field, or any one field, or no field. If he wants to search for all then he would just have to leave the textbox blank.
The logic to do this can be written as follows:
var p;
if(Name_TextBox=='')
{
p=from row in person
select row ;
}
else
{
p= from row in person
where row.Name=Name_TextBox
select row ;
}
// repeat the same for age
Now after a while the code gets very long and messy... How can I compress the above into a single query with no if-else?
Try code like this
string personName = txtPersonName.Text;
int personAge = Convert.ToInt32(txtAge.Text);
var opportunites = from p in this.DataContext.Persons
select new
{
p.PersonID,
p.Name,
p.Age,
p.Gender
};
if (personsID != 0)
opportunites = opportunites.Where(p => p.PersonID == personID);
if (personName != string.Empty)
opportunites = opportunites.Where(p => p.Name.StartsWith(personName));
if (personAge != 0)
opportunites = opportunites.Where(p => p.Age == personAge);
This will work fine. If personName is not given it will be not add to where, and if given then it will added.
One alternative which I have used in SQL which could be implemented in Linq too is
var p = from p in Person
where p.Name == Name_TextBox || Name_TextBox == String.Empty
select p;
(Note that your 'linq' is using SQL syntax, which won't compile. Also you can't declare a var as you are doing without directly assigning a value)
why not use the null coalescing operator? eg.
var products = from a in context.products
where a.ID == (productID ?? a.ID)
select a;
This works really well on my system

Resources