How to write a generic LINQ query for filtering of data based on first name middle name and last name - entity-framework

I have one table which contains employees records like first , middle, last name. I have 3 textboxes for entering all these.
Now on .net side I want to write a single LINQ query to filter data based on first name middle name and last name. Any of these 3 fields can be blank.
Is these any way to write a single generic LINQ query?

public IList<Employee> GetEmployees(string first, string middle, string last)
{
var query = context.Employees.AsQueryable();
if (!string.IsNullOrWhiteSpace(first))
{
query = query.Where(x => x.FirstName == first);
}
if (!string.IsNullOrWhiteSpace(middle))
{
query = query.Where(x => x.MiddleName == middle);
}
if (!string.IsNullOrWhiteSpace(last))
{
query = query.Where(x => x.LastName == last);
}
return query.Select(x =>
new Employee
{
FullName = string.Join(" ", new string[] { x.FirstName, x.MiddleName, x.LastName}.Where(y => !string.IsNullOrWhiteSpace(y)))
})
.ToList();
}

You can write one linq query with or condition like bellow
_context.tablename.where(p=>p.firstName.contains(txtFirstName) || p.middleName.contains(txtMiddleName) || p.lastName.contains(txtLastName)).ToList();
Change tablename with you database table in above linq query

when iterating just verify if the criteria in use has value
var criteria = new
{
FirstName = default(string),
MiddleName = default(string),
LastName = "Doe",
};
var query = from record in Context()
where !string.IsNullOrEmpty(criteria.FirstName)
&& record.FirstName == criteria.FirstName
|| !string.IsNullOrEmpty(criteria.MiddleName)
&& record.MiddleName == criteria.MiddleName
|| !string.IsNullOrEmpty(criteria.LastName)
&& record.LastName == criteria.LastName
select record;

Try something like that;
public List<Employee> RetrieveEmployees(string firstName, string lastName, string middleName)
{
var query = from employees in context.Employees
where (string.IsNullOrEmpty(firstName) || employees.FirstName == firstName) &&
(string.IsNullOrEmpty(lastName) || employees.LastName == lastName) &&
(string.IsNullOrEmpty(middleName) || employees.MiddleName == middleName)
select employees;
return query.ToList();
}

Related

Inline T-SQL OR subquery

Trying to figure out what would be the most efficient approach to use a subquery in an inline SQL statement. Inline SQL is not something I have much experience with, but I have no choice at my organization alas.
SELECT *
FROM dbo.VW_RMISPayment
WHERE ProcDate BETWEEN '7/2/2018' AND '3/8/2019'
AND Status = 'P'
AND Fund = '359'
AND Amount > 0
AND (BatchNotate = 'B' OR BatchNotate IS NULL)
ORDER BY ProcDate, Amount
How could I parameterized the (BatchNotate = 'B' OR BatchNotate IS NULL) part?
My variable passed in as a List<string>, but I could change it to be anything. I'm just not sure how I can create this subquery from my variable
if (BatchNotate.Count() > 0)
{
query += " AND BatchNotate= #BatchNotate";
}
cmd.Parameters.AddWithValue("#BatchNotate", batchNotate);
Use this:
BatchNotate = COALESCE(#inVariable,'B')
If the variable (#inVariable) is null then it will "default to B.
If it is something else it will compare against that.
Could you do something like this?
SELECT *
FROM dbo.VW_RMISPayment
WHERE ProcDate BETWEEN '7/2/2018' AND '3/8/2019'
AND Status = 'P'
AND Fund = '359'
AND Amount > 0
AND BatchNotate = COALESCE(#BatchNotate, BatchNotate)
ORDER BY ProcDate, Amount
Neither of those worked for what I'm after. Here is what I ended up doing. It's kinda hacky, but it works.
public static string AddParametersOR<T>(SqlParameterCollection parameters,
string fieldName,
string pattern,
SqlDbType parameterType,
int length,
IEnumerable<T> values)
{
if (parameters == null)
throw new ArgumentNullException("parameters");
if (pattern == null)
throw new ArgumentNullException("pattern");
if (values == null)
throw new ArgumentNullException("values");
if (!pattern.StartsWith("#", StringComparison.CurrentCultureIgnoreCase))
throw new ArgumentException("Pattern must start with '#'");
var parameterNames = new List<string>();
foreach (var item in values)
{
var parameterName = parameterNames.Count.ToString(pattern, CultureInfo.InvariantCulture);
string parameterWithFieldName = string.Empty;
if (item.ToString().ToUpper() == "NULL")
{
parameterWithFieldName = string.Format("{0} IS NULL", fieldName);
}
else if (item.ToString().ToUpper() == "NOTNULL")
{
parameterWithFieldName = string.Format("{0} IS NOT", fieldName);
}
else
{
parameterWithFieldName = string.Format("{0}= {1}", fieldName, parameterName);
}
parameterNames.Add(parameterWithFieldName);
parameters.Add(parameterName, parameterType, length).Value = item;
}
return string.Join(" OR ", parameterNames.ToArray());
}
Usage:
if (batchNotate.Count() > 0)
{
query += " AND ({#BatchNotate})";
}
string batchNotateParamNames = SqlHelper.AddParametersOR(cmd.Parameters, "BatchNotate", "#B0", SqlDbType.VarChar, 1, batchNotate);
cmd.CommandText = query.Replace("{#BatchNotate}", batchNotateParamNames);
Depending on how many items are in your list the output will look like this:
(BatchNotate= 'B' OR BatchNotate= 'N' OR BatchNotate IS NULL)
If it finds "NULL" or "NOTNULL" it will replace these with IS NULL or IS NOT NULL

Entity Framework get single row by LastOrDefault

I have a table named Table1 which has the following columns
ID, Foo, Bar, DateTime
ID is Primary Key
I am trying to get from the table the last row that has foo and bar that are coming as
parameters.
I want to get only the the most recent row (sorted by DateTime)
public void DoIt(string foo, string bar)
{
using (var context = new DataModelContainer())
{
var obj = (from m in context.Table1
where m.Foo == foo && m.Bar == bar
select m).LastOrDefault();
if (obj != null)
{
DoSomthing()
context.SaveChanges();
}
}
}
LastOrDefault is not supported in linq to sql. Use orderby .. descending then FirstOrDefault.
var obj = (from m in context.Table1
where m.Foo == foo && m.Bar == bar
orderby m.DateTime descending
select m).FirstOrDefault();

Property or indexer 'AnonymousType#1.FilePath' cannot be assigned to

Basically I retrieved the records from the table and wanted to updated one column.
var query = cdrContext.tabless.Where(c => c.FacilityID == facilityID && c.FilePath != null && c.TimeStationOffHook < oldDate)
.OrderBy(c => c.TimeStationOffHook)
.Skip(size)
.Take(pageSize)
.Select(c => new { c.FilePath, c.FileName })
.ToList();
So this query has only two fields: FilePath and FileName, then next I want to assign FilePath = null;
foreach (var y in query)
{
y.FilePath = null;
}
cdrContext.SaveChanges();
Then I got an error:
Property or indexer 'AnonymousType#1.FilePath' cannot be assigned to -- it is read only
You select from query anonymous class. You cannot set properties of anonymous class. To do what you want you should get whole entity:
var query = cdrContext.tabless.Where(c => c.FacilityID == facilityID && c.FilePath != null && c.TimeStationOffHook < oldDate)
.OrderBy(c => c.TimeStationOffHook)
.Skip(size)
.Take(pageSize)
.ToList();
foreach (var y in query)
{
y.FilePath = null;
}
cdrContext.SaveChanges();

entity framework: conditional filter

Let's say I have Customers table and I want to filter it by the following:
Country: All, US, UK, Canada
Income: All, low, high, medium
Age:All, teenager, adult, senior
if I had to build an SQL string for this filter, it would be something like this:
if (Country != "All") sql += "country = " + Country
if (Income != "All") sql += "and income = " + Income
if (Age != "All") sql += "and age = " + Age;
So, basically, the user can filter by some, but not necessary all fields.
How do you do this using Entity Framework ?
Thanks !
LINQ to Entity queries return IQueryable's, so you can build your query this way:
IQueryable<Person> query = context.People;
if (Country != "All")
{
query = query.Where(p => p.Country == Country);
}
if (Income != "All")
{
query = query.Where(p => p.Income == Income);
}
if (Age != "All")
{
query = query.Where(p => p.Age == Age);
}
List<Person> fetchedPeople = query.ToList();
This case is almost too simple, but this is very helpful in more complex situations when you need to add filtering dynamically.
You can include conditional parameter this way:
return Customers.Where(
customer =>
customer.Name == Name &&
(Age == "All" || customer.Age == Age) &&
(Income == "All" || customer.Income == Income) &&
(Country == "All" || customer.Country == Country)
).ToList();
If some condition is true (e.g. country is equal to All), then all parameter condition becomes true, and this parameter does not filter result.
You can use extension method to help readable and maintainable code.
LinqExtensions.cs
public static class LinqExtensions
{
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, bool>> predicate)
{
return condition ? source.Where(predicate) : source;
}
}
Refactoring code
List<Person> peoples = context.People
.WhereIf(Country != "All", p => p.Country == Country)
.WhereIf(Income != "All", p => p.Income == Income)
.WhereIf(Age != "All", p => p.Age == Age)
.ToList();

conditional where in linq query

I want to return the total sum from a linq query, I pass in 2 parameters that may/may not be included in the query.
OrgId - int
reportType - int
So two questions:
How can I update the query below so that if OrgId = 0 then ignore the organisation field(Return All)?
LocumClaimTF can be True/False/Both, if both then ignore the where query for this field.
Heres what I have done so far, this is working but I'd like something for efficient.
// Using reportType set preferences for LocumClaimTF
bool locumClaimTF1, locumClaimTF2 = false;
if (reportType == 0)
{
locumClaimTF1 = false;
locumClaimTF2 = false;
}
else if (reportType == 1)
{
locumClaimTF1 = true;
locumClaimTF2 = true;
}
else // 2
{
locumClaimTF1 = true;
locumClaimTF2 = false;
}
if (OrgID != 0) // Get by OrgID
{
return _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate)
.Where(x => x.Shift.LocumClaimTF == locumClaimTF1 || x.Shift.LocumClaimTF == locumClaimTF2)
.Where(x => x.Shift.organisationID == OrgID)
.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO { dataLabel = string.Concat(g.FirstOrDefault().User.FullName), dataCount = g.Count(), dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) }
).Sum(g=>g.dataCurrencyAmount);
}
else // Ignore OrgID - Get ALL Orgs
{
return _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate)
.Where(x => x.Shift.LocumClaimTF == locumClaimTF1 || x.Shift.LocumClaimTF == locumClaimTF2)
.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO { dataLabel = string.Concat(g.FirstOrDefault().User.FullName), dataCount = g.Count(), dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) }
).Sum(g => g.dataCurrencyAmount);
}
I'm using EF with unit of work pattern to get data frm
A few things come to mind, from top to bottom:
For handling the Booleans
bool locumClaimTF1 = (reportType == 1 || reportType == 2);
bool locumClaimTF2 = (reportType == 1);
From what I read in the query though, if the report type is 1 or 2, you want the Shift's LocumClaimTF flag to have to be True. If that is the case, then you can forget the Boolean flags and just use the reportType in your condition.
Next, for composing the query, you can conditionally compose where clauses. This is a nice thing about the fluent Linq syntax. However, let's start temporarily with a regular DbContext rather than the UoW because that will introduce some complexities and questions that you'll need to look over. (I will cover that below)
using (var context = new ApplicationDbContext()) // <- insert your DbContext here...
{
var query = context.ShiftDates
.Where(x => x.shiftStartDate >= StartDate
&& x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO
{
dataLabel = tring.Concat(g.FirstOrDefault().User.FullName),
dataCount = g.Count(),
dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value)
})
.Sum(g=>g.dataCurrencyAmount);
}
Now this here didn't make any sense. Why are you going through the trouble of grouping, counting, and summing data, just to sum the resulting sums? I suspect you've copied an existing query that was selecting a DTO for the grouped results. If you don't need the grouped results, you just want the total. So in that case, do away with the grouping and just take the sum of all applicable records:
var total = query.Sum(x => x.shiftDateTotal.Value);
So the whole thing would look something like:
using (var context = new ApplicationDbContext()) // <- insert your DbContext here...
{
var query = context.ShiftDates
.Where(x => x.shiftStartDate >= StartDate
&& x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.Sum(x => x.shiftDateTotal.Value);
return total;
}
Back to the Unit of Work: The main consideration when using this pattern is ensuring that this Get call absolutely must return back an IQueryable<TEntity>. If it returns anything else, such as IEnumerable<TEntity> then you are going to be facing significant performance problems as it will be returning materialized lists of entities loaded to memory rather than something that you can extend to build efficient queries to the database. If the Get method does not return IQueryable, or contains methods such as ToList anywhere within it followed by AsQueryable(), then have a talk with the rest of the dev team because you're literally standing on the code/EF equivalent of a land mine. If it does return IQueryable<TEntity> (IQueryable<ShiftDate> in this case) then you can substitute it back into the above query:
var query = _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.Sum(x => x.shiftDateTotal.Value);
return total;