Build string for Linq WHERE clause depending on multiple checkbox choices - linq

I can build the WHERE clause with a varying number of "AND"s in a string in PHP and then concatenate it to the end of my SELECT statement before running. I am trying to figure out how to do this in MVC Controller ActionResult event using LINQ
Usually something like
var strSQL ="SELECT field1, field3, field5 FROM tblWhatever WHERE 1-1"
(the 1=1 allows flexibilty in how many AND's I build, not having to worry about the first one)
then I build the string of ANDs or just one and concatenate
strSQL .= " AND DWPCEU != null "
strSQL .=" AND DEQCEU !=null "
then I run the SQL (in PHP)
Got any idea how I would build a string of ANDs in a Controller ActionResult?
this limited example below works, but only for one AND. I know I can add "&&"s ad nauseum
but would rather build it elsewhere since there are 4x4 number of checkbox choices.
There are 4 checkboxes and none, any, or all can be checked.
var courses = from m in _db.Courses select m;
if (!String.IsNullOrEmpty(searchString))
{
if (bolDWP == true)
{
courses = courses.Where(s => s.CourseTitle.Contains(searchString) **&& s.CEUDWP !=null**);
}
else
{
courses = courses.Where(s => s.CourseTitle.Contains(searchString));
}
}

I'm guessing you want to do something like this:
var courses = _db.Courses.AsQueryable();
if (!String.IsNullOrEmpty(searchString))
{
courses = courses.Where(s => s.CourseTitle.Contains(searchString));
}
if (bolDEQ)
{
courses = courses.Where(s => s.CEUDEQ != null);
}
if (bolDWP)
{
courses = courses.Where(s => s.CEUDWP != null);
}

Related

LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method

I've looked at the various solutions here but none of them seem to work for me, probably because I'm too new to all this and am groping in the dark a bit. In the code below, the object "appointment" contains some basic LDAP information. From a list of such objects I want to be able to get a single record, based on employee id. I hope the code here is sufficient to illustrate. FTR, I've tried various formulations, including trying to use from and a select. All fail with the error given in the Title above.
IQueryable<appointment> query = null;
foreach(var record in results)
{
BoiseStateLdapDataObject record1 = record;
query = db.appointments.Where(x => x.student_id == record1.Values["employeeid"]);
}
if (query != null)
{
var selectedRecord = query.SingleOrDefault();
}
Try to move employee id getting out of query:
IQueryable<appointment> query = null;
foreach(var record in results)
{
var employeeId = record.Values["employeeid"];
query = db.appointments.Where(x => x.student_id == employeeId);
}
if (query != null)
{
var selectedRecord = query.SingleOrDefault();
}

Entity Framework - LINQ selection in POCO generic List property

I'm having some issues setting a generic list property of a POCO object when from an EF context. For instance I have a very simple object that contains the following:
public class foo
{
public string fullName;
public Entity entity;
public List<SalesEvent> eventList;
}
My code to populate this object from looks something like this:
.Select(x => new foo()
{
fullName = x.vchFirstName + " " + x.vchLastName,
entity = new EntityVo()
{
address1 = x.vchAddress1,
entityId = x.iEntityId,
emailAddress = x.vchEmailAddress,
firstName = x.vchFirstName,
lastName = x.vchLastName,
city = x.vchCity,
state = x.chState,
workNumber = x.vchWorkNumber,
mobileNumber = x.vchMobileNumber,
siteId = x.iSiteId
}
eventList = _context.Events
.Where(e => e.iEntityId == x.iEntityId
&& e.iStatusId >= eventStatusMin
&& e.iStatusId <= eventStatusMax)
.Select(e => new List<SalesEventMatchVo>
{
new SalesEventMatchVo()
{
vehicleName = _context.Quotes.Select(q=>q).Where(q=>q.iEventId == e.iEventId).FirstOrDefault().vchMake + " " + _context.Quotes.Select(q=>q).Where(q=>q.iEventId == e.iEventId).FirstOrDefault().vchModel,
eventId = e.iEventId,
salesPerson = e.chAssignedTo,
eventStatusDesc=_context.RefDefinitions.Select(r=>r).Where(r=>r.iParameterId==e.iStatusId).FirstOrDefault().vchParameterDesc,
eventStatusId =(int)e.iStatusId,
eventSourceDesc=_context.RefDefinitions.Select(r=>r).Where(r=>r.iParameterId==e.iSourceId).FirstOrDefault().vchParameterDesc,
createDate = e.dtInsertDate
}
}).FirstOrDefault()
}).ToArray();
This issue I'm having is that I'm unable to populate the eventList property with all of the events, it's only grabbing the first record(which makes sense looking at the code). I just cant seem to figure out to populate a the entire list.
Is there a reason simply removing the FirstOrDefault at the end isn't the solution here? I feel like I might be misunderstanding something.
EDIT:
I think I see what you are trying to do. The issue is that you are creating a list in the select statement, when the select statement works only over one thing at a time. It is basically mapping an input type to a new output type.
Try something like this instead:
eventList = _context.Events.Where(e => e.iEntityId == x.iEntityId && //FILTER EVENTS
e.iStatusId >= eventStatusMin &&
e.iStatusId <= eventStatusMax)
.Select(e => new SalesEventMatchVo() //MAP TO SALESEVENT
{
vehicleName = _context.Quotes.Select(q=>q).Where(q=>q.iEventId == e.iEventId).FirstOrDefault().vchMake + " " + _context.Quotes.Select(q=>q).Where(q=>q.iEventId == e.iEventId).FirstOrDefault().vchModel,
eventId = e.iEventId,
salesPerson = e.chAssignedTo,
eventStatusDesc=_context.RefDefinitions.Select(r=>r).Where(r=>r.iParameterId==e.iStatusId).FirstOrDefault().vchParameterDesc,
eventStatusId =(int)e.iStatusId,
eventSourceDesc=_context.RefDefinitions.Select(r=>r).Where(r=>r.iParameterId==e.iSourceId).FirstOrDefault().vchParameterDesc,
createDate = e.dtInsertDate
})
.ToList() //CONVERT TO LIST
As a side note, unless you actually need a List for some reason, I would store foo.eventList as IEnumerable<SalesEvent> instead. This allows you to skip the List conversion at the end, and in some scenarios enables neat tricks like delayed and/or partial execution.
Also, I'm not sure what the point of your .Select(q=>q) statements are in several lines of the SalesEventMatchVo initializer, but I'm pretty sure you can chop them out. If nothing else, you should Select after Where, as Where can reduce the work performed by all following statements.

Linq - Simulate an OrWhere expression when building LINQ queries dynamically?

The code snippet below search allow the user to match a string against three fields in the table. If any of the fields match, the entry is included in the result. However, using Where to filter out the results is resulting in "the string must match all three fields" instead "the string can match any of the three fields".
Is there a way to simulate an OrWhere expression when building LINQ queries dynamically?
var foundUsers = from UserInfo user in entities.UserInfo
select user;
if (searchCompleteName)
{
foundUsers = foundUsers.Where(u => u.CompleteName.Contains(searchString));
}
if (searchPortalID)
{
foundUsers = foundUsers.Where(u => u.PortalID.Contains(searchString));
}
if (searchUsername)
{
foundUsers = foundUsers.Where(u => u.UserIdentity.Contains(searchString));
}
PS. I am using Entities Framework and LINQ to Entities, and am doing a MVC3 Web Application.
Try this:- http://www.albahari.com/nutshell/predicatebuilder.aspx
Not exactly pretty, but it would work.
var foundUsers = entities.UserInfo.Where(u =>
(searchCompleteName && u.CompleteName.Contains(searchString))
|| (searchPortalID && u.PortalID.Contains(searchString))
|| (searchUsername && u.UserIdentity.Contains(searchString));
You could also do this with a union. The union operator returns distinct results so there will not be any duplicates. I have no idea if EF can defer this to the database.
var foundUsers = Enumerable.Empty<UserInfo>().AsQueryable();
if (searchCompleteName)
{
foundUsers = foundUsers.Union(entities.UserInfo.Where(u => u.CompleteName.Contains(searchString)));
}
if (searchPortalID)
{
foundUsers = foundUsers.Union(entities.UserInfo.Where(u => u.PortalID.Contains(searchString)));
}
if (searchUsername)
{
foundUsers = foundUsers.Union(entities.UserInfo.Where(u => u.PortalID.Contains(searchString)));
}

Extracting text from an IEnumerable<T>

I have a IEnumerable<T> collection with Name, FullName and Address.
The Address looks like this:
Street1=54, Street2=redfer street, Pin=324234
Street1=54, Street2=fdgdgdfg street, Pin=45654
Street1=55, Street2=tryry street, Pin=65464
I want to loop through this collection and print only those Names, FullNames whose Street1=54
How can i do it in LINQ?
Ok I was able to do this to extract Street1 of the Address
coll.Address.Split(",".ToCharArray())[0]returns me Street1=54 .
Now how do I add this to the condition and print only those Name, FullName whose Street1=54
Based on your update, you can adapt Jared Par's code this way:
var result = collection.Where(x => x.Address.Contains("Street1=54"));
foreach ( var cur in result ) {
Console.WriteLine(string.Format("{0}, {1}", cur.Name, cur.FullName));
}
If you want to be able to plug in your Street1 value with a variable, then do this:
var street1 = "54";
var result = collection.Where(x => x.Address.Contains("Street1=" + street1 ));
foreach ( var cur in result ) {
Console.WriteLine(string.Format("{0}, {1}", cur.Name, cur.FullName));
}
BTW, you really should update your question or add a comment to a specific answer rather than adding a new answer that isn't.
Try this
var result = collection.Where(x => x.Address.Street1==54);
foreach ( var cur in result ) {
Console.WriteLine(var.Name);
}
Select the correct list of :
IList<T> matches = myListOfEnumerables.Where(m => m.Street1 == 54).ToList();
Then loop and print.
Actually the record looks like this:
{Name="Jan" FullName="Kathy Jan" Address="Street1=54, Street2=redfer street, Pin=324234"}
I have to loop through this collection and print only those Names, FullNames whose Street1=54
If the updated information is accurate, you should change the way you store the data.
It looks like you've packed the address information into a string. Why not store it as an object. In fact, why not just as more fields in the same object as the Name and FullName? (and why duplicate the first-name information?)
public class Person
{
public string FirstName, LastName, Street1, Street2, Pin;
}
IEnumerable<Person> persons = GetAllPersonsSomehow();
foreach (Person person in persons.Where(p => p.Street1 == "54"))
Console.WriteLine(person.LastName + ", " + person.FirstName);
Assuming you have to keep the address information in a string, you need a parser for it.
public static IDictionary<string, string> GetAddressFields(string address)
{
return address.Split(',').ToDictionary(
s => s.Substring(0, s.IndexOf('=')).Trim(),
s => s.Substring(s.IndexOf('=') + 1).Trim());
}
foreach (Person person in persons.Where(p =>
GetAddressFields(p.Address)["Street1"] == "54"))
Console.WriteLine(person.LastName + ", " + person.FirstName);
So, what you could do is that you could write a generator for parsing the Address field and then enumerating properties of that. This is a fairly common thing in the functional programming world.
To be fair you would want this code to be lazy in that it would only compute a minimal set. I'm gonna suggest some code from the BCL but you can (and probably should) rewrite the same helper methods with generators.
public static IEnumerable<KeyValuePair<string,string>> NameValueSplit( this string s )
{
foreach (var x in s.Split(','))
{
var y = x.Split(new char[] { '=' }, 2, StringSplitOptions.None);
yield return new KeyValuePair<string, string>(y[0].TrimStart(), y[1].TrimEnd());
}
}
With that helper function you can write code like this
var result = collection.Where(x => x.Address
.NameValueSplit().Any(x => x.Key == "Street1" && x.Value == "54"));
foreach ( var item in result )
{
Console.WriteLine(item.Name);
}
Now this code will not run on your SQL Server if you were thinking of that, but you could write a WHERE clause where you would search the Address field for a sub string %Street1=54%. I like lazy evaluation for string operations and think that's a lacking feature in the BCL. That is why I suggested that kind of solution.

Multiple "order by" in LINQ

I have two tables, movies and categories, and I want to get an ordered list by categoryID first and then by Name.
The movie table has three columns ID, Name and CategoryID.
The category table has two columns ID and Name.
I tried something like the following, but it didn't work.
var movies = _db.Movies.OrderBy( m => { m.CategoryID, m.Name })
This should work for you:
var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)
Using non-lambda, query-syntax LINQ, you can do this:
var movies = from row in _db.Movies
orderby row.Category, row.Name
select row;
[EDIT to address comment] To control the sort order, use the keywords ascending (which is the default and therefore not particularly useful) or descending, like so:
var movies = from row in _db.Movies
orderby row.Category descending, row.Name
select row;
Add "new":
var movies = _db.Movies.OrderBy( m => new { m.CategoryID, m.Name })
That works on my box. It does return something that can be used to sort. It returns an object with two values.
Similar, but different to sorting by a combined column, as follows.
var movies = _db.Movies.OrderBy( m => (m.CategoryID.ToString() + m.Name))
Use the following line on your DataContext to log the SQL activity on the DataContext to the console - then you can see exactly what your LINQ statements are requesting from the database:
_db.Log = Console.Out
The following LINQ statements:
var movies = from row in _db.Movies
orderby row.CategoryID, row.Name
select row;
AND
var movies = _db.Movies.OrderBy(m => m.CategoryID).ThenBy(m => m.Name);
produce the following SQL:
SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].CategoryID, [t0].[Name]
Whereas, repeating an OrderBy in LINQ, appears to reverse the resulting SQL output:
var movies = from row in _db.Movies
orderby row.CategoryID
orderby row.Name
select row;
AND
var movies = _db.Movies.OrderBy(m => m.CategoryID).OrderBy(m => m.Name);
produce the following SQL (Name and CategoryId are switched):
SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].[Name], [t0].CategoryID
I have created some extension methods (below) so you don't have to worry if an IQueryable is already ordered or not. If you want to order by multiple properties just do it as follows:
// We do not have to care if the queryable is already sorted or not.
// The order of the Smart* calls defines the order priority
queryable.SmartOrderBy(i => i.Property1).SmartOrderByDescending(i => i.Property2);
This is especially helpful if you create the ordering dynamically, f.e. from a list of properties to sort.
public static class IQueryableExtension
{
public static bool IsOrdered<T>(this IQueryable<T> queryable) {
if(queryable == null) {
throw new ArgumentNullException("queryable");
}
return queryable.Expression.Type == typeof(IOrderedQueryable<T>);
}
public static IQueryable<T> SmartOrderBy<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
if(queryable.IsOrdered()) {
var orderedQuery = queryable as IOrderedQueryable<T>;
return orderedQuery.ThenBy(keySelector);
} else {
return queryable.OrderBy(keySelector);
}
}
public static IQueryable<T> SmartOrderByDescending<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
if(queryable.IsOrdered()) {
var orderedQuery = queryable as IOrderedQueryable<T>;
return orderedQuery.ThenByDescending(keySelector);
} else {
return queryable.OrderByDescending(keySelector);
}
}
}
There is at least one more way to do this using LINQ, although not the easiest.
You can do it by using the OrberBy() method that uses an IComparer. First you need to
implement an IComparer for the Movie class like this:
public class MovieComparer : IComparer<Movie>
{
public int Compare(Movie x, Movie y)
{
if (x.CategoryId == y.CategoryId)
{
return x.Name.CompareTo(y.Name);
}
else
{
return x.CategoryId.CompareTo(y.CategoryId);
}
}
}
Then you can order the movies with the following syntax:
var movies = _db.Movies.OrderBy(item => item, new MovieComparer());
If you need to switch the ordering to descending for one of the items just switch the x and y inside the Compare()
method of the MovieComparer accordingly.
If use generic repository
> lstModule = _ModuleRepository.GetAll().OrderBy(x => new { x.Level,
> x.Rank}).ToList();
else
> _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList();

Resources