LINQ - Buidling a search query - linq

I am building up a search query for my customer search function:
I have all these fields passing into the function and wonder what is the best way to build up the LINQ expression. Some of the fields maybe an empty string and the search should be using "contains" instead of searching the exact field string
public List<Customer> SearchCustomer(
string membershipID,
string preferName,
string firstName,
string lastName,
string nric,
string phoneNumber,
string email,
DateTime dob,
string gender,
string address,
Boolean vip,
bool isDeleted)

You can manage multiple filter parameters in the following way:
var result = customerCollection.
.Where(c => membershipID != null ? c.membershipId.Contains(membershipID) : true)
.Where(c => preferName != null ? c.preferName.Contains(membershipID) : true)
...
.ToList();
I hope you get the idea

Related

System.InvalidOperationException: 'Sequence contains no matching element'

May I know why that error keeps pointing to "String isEmailVerified ...."?
public JsonResult GetMemberCounts([FromBody] ChartFilterRequest filter)
{
DateTime startDate = DateTime.Parse(filter.MainFilter.First(m => m.Name == "startDate").Value as string);
DateTime endDate = DateTime.Parse(filter.MainFilter.First(m => m.Name == "endDate").Value as string);
String isEmailVerified = filter.MainFilter.First(m => m.Name == "isEmailVerified").Value as string;
var data = _dashboardComponent.GetMemberCount(startDate, endDate, isEmailVerified);
return new JsonResult(data);
}
Try using FirstOrDefault and LastOrDefault instead of First and Last, these methods will return the default value of the type they are invoked for if no elements match the lambda expression you provide as a parameter.
In your project, You just use filter.MainFilter.First(xxxx) to select the data, So if no elements match the lambda expression you provide as a parameter,First() will throw an exception, So here will report this error.

Complex OrderBy in Linq

I have an list of DemoInfo object with list of StatInfo object in it as below-:
public class DemoInfo
{
public string name;
public List<StatInfo> stat;
public string workLocation;
}
Further StatInfo is below object
public class StatInfo
{
public string contact;
public DateTime createdDate;
public string action;
}
I need to orderby list of DemoInfo by createdDate
How can I do that in Linq?
Your problem is because you haven't got a proper specification
I need to orderby list of DemoInfo by createdDate
You can't do that, becuase a DemoInfo has no CreatedDate. If you assume that a DemoInfo was created the day that it got its first StatInfo then you could use the oldest StatInfo to order:
I need to order my sequence of DemoInfo in ascending order by its creation date, which is defined as the date of its oldest StatInfo.
This still has a problem: what is the CreatedDate of a DemoInfo after all its StatInfos are removed? And removing the oldest StatInfo of a DemoInfo with many StatInfos suddenly changes the CreatedDate of the DemoInfo.
But let's assume that that's no problem for you, and you want all DemoInfo without any StatInfo last in your result (because they will get their CreationDate in future, when it gets its first StatInfo. Your query will be like:
var result = myDemoInfos.Select(demoInfo => new
{
DemoInfo = demoInfo,
// if the demoInfo has a non-null stat and a non-empty stat
// order it by ascending StatInfo.CreatedDate, and take the first
// otherwise use DateTime.MaxValue (Created in far future)
CreationDate = ( (demoInfo.stat != null) && (demoInfo.stat.Any()) ?
demoInfo.Stat
.Select(statInfo => createdDate)
.OrderBy(createdDate => createdDate)
.First() : // you know there is a first, you just checked Any()
DateTime.MaxValue, // if there is no First, take far future
})
.OrderBy(item => item.CreationDate)
.Select(item => item.DemoInfo);
This can be smarter: you don't have to sort all createdDates, you only need the createdDate with the smallest TickCount:
TickCount = ( (demoInfo.stat != null) && (demoInfo.stat.Any()) ?
demoInfo.stat.Select(statInfo => statInfo.createdDate.TickCount).Min() :
DateTime.MaxValue.TickCount,
And sort by ascending TickCount

Advanced search with LINQ to EF

I have up to 4 values: string firstName, string lastName, string ssn, DateTime dateOfInjury. The user can enter any one of these or any combination. If they enter more than one, I want to return results using AND. For example, where firstName matches (if that's all they enter), or if firstName AND lastName match if they enter both of them, and so on. What's the best way to do this with LINQ? I'm hoping there's something more elegant that a giant switch statement.
I was planning on building the where clause dynamically, but it doesn't seem like that will work: Building dynamic where clauses in LINQ to EF queries.
Just build up your query. If you have a sequence of Where calls, those are AND'd together.
IQueryable<Customer> query = source.Customers;
if (firstName != null)
{
query = query.Where(c => c.FirstName == firstName);
}
if (lastName != null)
{
query = query.Where(c => c.LastName == lastName);
}

Linq Order by when column name is dynamic and pass as a string to a function

I have a Linq (Entity Framework) Query as
function getData(string col_to_sort , bool IsAscending , int pageNo , int pageSize)
{
context.table_name.Skip(pageNo*pageSize).Take(pageSize).ToArray();
}
What i want is that if i pass the name of the column as a parameter to the function
and the order it will sort my query too.
Since my column name will be a string so we might need to convert it to ObjectQuery.
How can i achieve this?
Any help is appreciated
You can use Dynamic Linq:
string direction = IsAscending ? " ASC" : " DESC";
context.table_name.OrderBy(col_to_sort + direction).Skip(pageNo*pageSize).Take(pageSize).ToArray();
If you are using Dynamic Linq, then the accepted answer will work.
But If you don't want to add an extra library (Dynamic Linq), then you can pick my first approach. I will explain both the approaches where you have or don't have Dynamic Linq. You can select based on your preferences and choice.
First Approach: When you don't have Dynamic Linq:
If you are using using System.Linq; instead of using System.Linq.Dynamic.Core, then you can use this approach:
orderBy is the string and Student is the T (The Entity, in which we want to search).
Create a Utility class, something like this: (you can anytime covert to extension method If you wish.)
public static class LinqUtility
{
public static Expression<Func<T, object>> ToLambda<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propertyName);
var propAsObject = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<T, object>>(propAsObject, parameter);
}
}
And you can use like this:
public async Task<IList<Student>> GetStudents(long groupId, string orderBy, Filter filter)
{
return await _context.Students.Where(x => x.StudentGroupId == groupId)
.OrderByDescending(LinqUtility.ToLambda<Student>(orderBy))
.Skip(filter.Skip)
.Take(filter.Take)
.ToListAsync();
}
Second Approach: When you have Dynamic Linq:
The Dynamic LINQ library exposes a set of extension methods on IQueryable corresponding to the standard LINQ methods at Queryable, and which accept strings in a special syntax instead of expression trees.
You need to include the Library separately. Include System.Linq.Dynamic.Core. The Author of this Library is not Microsoft.. As such no harm in using it.
So, this Library, provides you a method, that accepts the string.
You can pass comma separated values as well in string, this can be achieved by above code as well, but some changes would be required.
In the same example, just pass orderBy:
public async Task<IList<Student>> GetStudents(long groupId, string orderBy, Filter filter)
{
return await _context.Students.Where(x => x.StudentGroupId == groupId)
.OrderBy(orderBy)
.Skip(filter.Skip)
.Take(filter.Take)
.ToListAsync();
}

MVC3/LINQ/EF4.1 selecting distinct col values from a result set?

How can I select a list of column values from a result set (as distinct) and put into a list?
class T {int id; string name;}
-- Controller...
var query = #"exec someStoredProc";
IEnumerable<T> bb =
db2.Database.SqlQuery<T>(query);
// Something like???:
List<string> Names = bb.SelectDistinct("name"); // returns distinct list of names from result set
Since you just need the distinct list of names, you can project to the name property and the just use Distinct() :
List<string> Names = bb.Select( x=> x.name)
.Distinct()
.ToList();
This requires that you make the name property public, also I would rethink your class name T, how about CustomerName (or whatever else is expressive enough so you know what it means) ?
public class CustomerName
{
public int id{get;set;}
public string name {get;set;}
}

Resources