LINQ Method Syntax - how to accomplish dynamic Linq statements - c#

I use Linq to Entities to retrieve my records from DB. The function below is in a method. Method has some parameters (arguments) like group, datefrom, dateto, place, state , searchtext etc. etc.
the whole idea is if these parameters are not empty or null then accomplish the LINQ statments. The way I'm doing is I'm checking whether there is a value or not. if it has the value then I pass e.g. a.no_group= group if it doesn't has a value then I pass a statement like a.id!=-1, which is always true.
Problem:
I mean I'm not happy with passing in every statement like "a.id != -1" which is always true . I use this because I have to put a value there. But I'm not happy with this way of doing it... (it works!)
Question:
The question is: this right way of doing?
1- Can I replace a.id != -1 with something else ?
2- If you see the whole linq statement is duplicated because of language check. As you see the last
linq statement checks on language 'Dutch' or 'French'... How to avoid dupliation?
3- I'm checking whether the current date (datetime.now) is between date_begin and date_end. Is this correct way...
The whole code works fine, but I think I'm complicating the code which can be much simpler...
But how?
if (Language == ConfBouwHelper.LanguageEnum.French)
{
//FRENCH RECORDS
listAgendaItems = dc.agenda.Where(a =>
((String.IsNullOrEmpty(group)) ? (a.id != -1) : (a.no_group == group))
&& ((activityType.Equals("ALL")) ? (a.id != -1) : (a.type_manifestation == activityType))
&& ((String.IsNullOrEmpty(dateFrom)) ? (a.id != -1) : (a.date_debut.Value >= dateFrom))
&& ((String.IsNullOrEmpty(dateTo)) ? (a.id != -1) : (a.date_debut.Value <= dateTo))
&& ((String.IsNullOrEmpty(place)) ? (a.id != -1) : (a.emplacement.Contains(place)))
&& ((String.IsNullOrEmpty(state)) ? (a.id != -1) : (a.cd_prov == state))
&& ((String.IsNullOrEmpty(searchText)) ? (a.id != -1) : (a.libelle_activite.Contains(searchText)))
&& ((a.date_begin_display.HasValue ? DateTime.Now >= a.date_begin_display.Value : a.id != -1) &&
(a.date_end_display.HasValue ? DateTime.Now <= a.date_end_display.Value : a.id != -1))
&& (a.langue == "FRENCH" || a.langue == "B")).ToList<agenda>(); //GET FRENCH
}
else
//DUTCH RECORDS
{
listAgendaItems = dc.agenda.Where(a =>
((String.IsNullOrEmpty(group)) ? (a.id != -1) : (a.no_group == group))
&& ((activityType.Equals("ALL")) ? (a.id != -1) : (a.type_manifestation == activityType))
&& ((String.IsNullOrEmpty(dateFrom)) ? (a.id != -1) : (a.date_debut.Value >= dateFrom))
&& ((String.IsNullOrEmpty(dateTo)) ? (a.id != -1) : (a.date_debut.Value <= dateTo))
&& ((String.IsNullOrEmpty(place)) ? (a.id != -1) : (a.emplacement.Contains(place)))
&& ((String.IsNullOrEmpty(state)) ? (a.id != -1) : (a.cd_prov == state))
&& ((String.IsNullOrEmpty(searchText)) ? (a.id != -1) : (a.libelle_activite.Contains(searchText)))
&& ((a.date_begin_display.HasValue ? DateTime.Now >= a.date_begin_display.Value : a.id != -1) &&
(a.date_end_display.HasValue ? DateTime.Now <= a.date_end_display.Value : a.id != -1))
&& (a.langue == "DUTCH" || a.langue == "B")).ToList<agenda>(); //GET DUTCH
}

You can add multiple where clauses:
var query = dc.agenda;
if(!String.IsNullOrEmpty(group))
query = query.Where(a => a.no_group == group)
if(!activityType.Equals("ALL"))
query = query.Where(a => a.type_manifestation == activityType)
// and so on for all your conditions...
if (Language == ConfBouwHelper.LanguageEnum.French)
query = query.Where(a => (a.langue == "FRENCH" || a.langue == "B"));
else
query = query.Where(a => (a.langue == "DUTCH" || a.langue == "B"));
listAgendaItems = query.ToList<agenda>();
This is a lot cleaner and readable and also solves the problem with the duplication because of the languages.

Related

convert lambda expression to function

I wish to debug lambda expression but its not possible as it sets breakpoint on whole expression.
Here is my lambda expression
public bool CanRevert => _objectEntity != null && !(_objectEntity != null &&
ObjectDescription == _objectEntity.DTO.Description &&
ObjectInstance == _objectEntity.DTO.ObjectInstance.ToString() &&
Name == _objectEntity.DTO.ObjectName &&
(SelectedDevice != null && SelectedDevice.Id == _objectEntity.DTO.DeviceID) &&
(_objectEntity.DTO.ObjectCategoryID.HasValue ? (SelectedObjectCategory != null && SelectedObjectCategory.Id == _objectEntity.DTO.ObjectCategoryID.Value) : SelectedObjectCategory == null) &&
(_objectEntity.DTO.ObjectTypeID.HasValue ? (SelectedObjectType != null && SelectedObjectType.Id == _objectEntity.DTO.ObjectTypeID.Value) : SelectedObjectType == null));
I wanted to put breakpoint inside selectedDevice but i cant. Hence i tried to write it like
public bool CanRevert()
{
if (SelectedDevice != null)
{
var s = SelectedDevice.Id == _objectEntity.DTO.DeviceID;
}
var d = _objectEntity != null && !(_objectEntity != null &&
ObjectDescription == _objectEntity.DTO.Description &&
ObjectInstance == _objectEntity.DTO.ObjectInstance.ToString() &&
Name == _objectEntity.DTO.ObjectName &&
(SelectedDevice != null && SelectedDevice.Id == _objectEntity.DTO.DeviceID) &&
(_objectEntity.DTO.ObjectCategoryID.HasValue ? (SelectedObjectCategory != null && SelectedObjectCategory.Id == _objectEntity.DTO.ObjectCategoryID.Value) : SelectedObjectCategory == null) &&
(_objectEntity.DTO.ObjectTypeID.HasValue ? (SelectedObjectType != null && SelectedObjectType.Id == _objectEntity.DTO.ObjectTypeID.Value) : SelectedObjectType == null));
return d;
}
Is it right way to convert it? I ask because i am starting to get somewhere in application that Binding to this method is not supported
You can achieve the same result like this.
Put a breakpoint in your expression.
Start Debugging (F5)
Highlight (SelectedDevice != null && SelectedDevice.Id == _objectEntity.DTO.DeviceID) in your expression
Right-click and select Add Watch
Now you have its calculated value in the Watch window. (Usually pops up at the bottom)
You can use Quick Watch (Shift+F9) if you want its value once.

c# Multiple Independent Filters on Lambda Query

I've been working on this for hours today and feel like there is an easy way to do this but I am unable to make it work by anything but brute force.
I have an entity in my application that serves as a Mapping between two objects, based on 5 filters. The goal is to find the record with the most specific match to the filters.
Right now, I am manually brute forcing 2^5 queries to get the most specific row, but feel like there has to be a much easier way to do this.
The only gotcha here is that there may be no match in the database for a specific filter (or all), in which case I want to select the NULL record.
Below I have an excerpt of my embarrassing brute force query -- I first want to try and match on all 5 filters, then on permutations of 4 matching, then 3, then 2, then 1, and finally all Nulls.
incList.FirstOrDefault(x =>
x.Filter1 == filter1Parameter && x.Filter2 == filter2Parameter && x.Filter3 == filter3Parameter && x.Filter4 == filter4Parameter && x.Filter5 == filter5Parameter
|| x.Filter1 == null && x.Filter2 == filter2Parameter && x.Filter3 == filter3Parameter && x.Filter4 == filter4Parameter && x.Filter5 == filter5Parameter
|| x.Filter1 == filter1Parameter && x.Filter2 == null && x.Filter3 == filter3Parameter && x.Filter4 == filter4Parameter && x.Filter5 == filter5Parameter
|| x.Filter1 == filter1Parameter && x.Filter2 == filter2Parameter && x.Filter3 == null && x.Filter4 == filter4Parameter && x.Filter5 == filter5Parameter
|| x.Filter1 == filter1Parameter && x.Filter2 == filter2Parameter && x.Filter3 == filter3Parameter && x.Filter4 == null && x.Filter5 == filter5Parameter
|| x.Filter1 == filter1Parameter && x.Filter2 == filter2Parameter && x.Filter3 == filter3Parameter && x.Filter4 == filter4Parameter && x.Filter5 == null
I originally thought I could have a simple statement that would independently grab the value if present, and grab null if not
incList.FirstOrDefault(x => (x.Filter1 == filter1Parameter || x.Filter1 == null) &&
(x.Filter2 == filter2Parameter || x.Filter2 == null) &&
(x.Filter3 == filter3Parameter || x.Filter3 == null) &&
(x.Filter4 == filter4Parameter || x.Filter4 == null) &&
(x.Filter5 == filter5Parameter || x.Filter5 == null));
But that did not work.
Any pointers would be appreciated.
I'm still trying to understand the full extent of the requirement.
However, have you explored abstracting this Func or Func(s) in a separate class. This separate class or classes would operate like a Strategy. Only in charge of what filters a collection based on a certain predicate.
If that does not seem like a good route, what about looking into writing your own implementation IEqualityComparer. This will allow you determine what makes these objects equal.
You can introduce something like a "fitness function" and then select item with max fit value
var bestMatch = incList.Select(x => new
{
item = x,
fit =
(x.Filter1 == null
? 1
: (x.Filter1 == filter1Parameter ? 2 : 0)) *
(x.Filter2 == null
? 1
: (x.Filter2 == filter2Parameter ? 2 : 0)) // and so on
})
.OrderByDescending(x => x.fit)
.FirstOrDefault(x => x.fit > 0)?.item;

How do I get Linq to return identical repeated rows only once?

I'm still pretty new to this, so go easy on me. I'm trying to get linq to return a single record when the query returns multiple identical records. Here's what I have:
var query = (from i in _db.BizListView
let dist = DbGeography.FromText(i.Point).Distance(DbGeography.FromText(geog)) * .0006214
where
dist <= rng &&
i.Name.Contains(key) &&
pri != -1 ? (i.BizCategoryID == pri && i.Primary == true) : true &&
biz != -1 ? (i.BizCategoryID == biz) : true
group i by new
{
ClientID = i.ClientID,
Name = i.Name,
Address = i.Address,
Address1 = i.City + ", " + i.StateID + " " + i.PostCode,
BizPhone = i.BizPhone,
EmailAddress = i.EmailAddress,
ClientImagePath = i.ClientImagePath,
Distance = DbGeography.FromText(i.Point).Distance(DbGeography.FromText(geog)) * 0.0006214
}
into myTable
orderby myTable.Key.Distance ascending
select new
{
ClientID = myTable.Key.ClientID,
Name = myTable.Key.Name,
Address = myTable.Key.Address,
Address1 = myTable.Key.Address1,
BizPhone = myTable.Key.BizPhone,
EmailAddress = myTable.Key.EmailAddress,
ClientImagePath = myTable.Key.ClientImagePath,
Distance = myTable.Key.Distance
}).Distinct();
return query;
The query produces a single record for each client, as long as a BizCategory is specified, but returns multiple identical records per client (a record for each of the Client's BizCategories) if no BizCategory is specified. How can I get the query to return a single record per client, instead of multiple identical records when no BizCategory is specified?
I believe that operator precedence is affecting the query.
Try adding parentheses around the ternary operators.
(pri != -1 ? (i.BizCategoryID == pri && i.Primary == true) : true) &&
(biz != -1 ? (i.BizCategoryID == biz) : true)
A simpler way to express this condition could be:
(pri == -1 || i.BizCategoryID == pri) &&
(biz == -1 || i.BizCategoryID == biz)
The way it was interpreted:
pri != -1
? (i.BizCategoryID == pri && i.Primary == true)
: (true && (biz != -1 ? (i.BizCategoryID == biz) : true))
|< ----- IGNORED ----------------------------->|
You can see that the true && biz != -1... took precedence here. As true was evaluated first, the biz was never evaluated.
Gah!! All I had to do was add an additional conditional!
var query = from i in _db.BizListView
let dist = DbGeography.FromText(i.Point).Distance(DbGeography.FromText(geog)) * 0.0006214
where
dist <= rng &&
i.Name.Contains(key) &&
pri != -1 ? (i.BizCategoryID == pri && i.Primary == true) : true &&
biz != -1 ? (i.BizCategoryID == biz) : true &&
(biz == -1 && pri == -1) ? (i.Primary == true): true
orderby i.Distance ascending
select i;
Oh well. Live and learn.

Simple LINQ and/or query condition clarification

This is a very simple LINQ and/or question.
In the following query excerpt, my intention is to obtain records which match any of the three conditions below.
Is my logic correct or is there a better way to phrase this?
(t2.Username == userName) && (viewMode == 1)
(t1.Owner == userName) && (viewMode == 1)
(viewMode == 2)
Query excerpt
where
((t2.Username == userName) && (viewMode == 1)) ||
((t1.Owner == userName) && (viewMode == 1)) ||
((viewMode == 2))
You're logic looks fine, you could of course do it slightly differently:
where ( (viewMode == 1 && ( t2.Username == userName || t1.Owner == userName ))
|| viewMode == 2)
Whichever looks better to your eyes I guess!
This should get you the same result a little more efficiently:
viewMode == 2 ||
(viewMode == 1 && (t2.Username == userName || t1.Owner == userName))

LINQ - Multiple Where based on conditions

I want to query a table with some conditions based on user input.
I have this code:
IQueryable<Turno> turnoQuery = dc.Turno;
if (helper.FechaUltimaCitaDesde != DateTime.MinValue)
{
turnoQuery = turnoQuery.Where(t => t.TurnoFecha >= helper.FechaUltimaCitaDesde);
}
if (helper.FechaUltimaCitaHasta != DateTime.MinValue)
{
turnoQuery = turnoQuery.Where(t => t.TurnoFecha <= helper.FechaUltimaCitaHasta);
}
if (helper.SoloCitasConsumidas)
{
turnoQuery = turnoQuery.Where(t => t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Consumido));
}
else if(helper.AnuladoresDeCitas)
{
turnoQuery = turnoQuery.Where(t => t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Cancelado) || t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Ausente));
}
The problem I'm having is that the "where" clause gets overwritten with the last one.
Whats the correct way to do something like this on LINQ?
The "helper" object is a custom class storing the user input dates for this example.
You could combine the expressions by using a series of ternary operations. This isn't tested so there may be some syntax issues, but here's the basic idea:
turnoQuery = turnoQuery.Where(
t => t.TurnoFecha >= helper.FechaUltimaCitaDesde != DateTime.MinValue ? helper.FechaUltimaCitaDesde : DateTime.MinValue &&
t.TurnoFecha <= helper.FechaUltimaCitaHasta != DateTime.MinValue ? helper.FechaUltimaCitaHasta : DateTime.MaxValue &&
helper.SoloCitasConsumidas ? t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Consumido :
t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Cancelado) || t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Ausente) &&
helper.FechaUltimaCitaDesde != DateTime.MinValue ? t.TurnoFecha >= helper.FechaUltimaCitaDesde : t.TurnoFecha <= helper.FechaUltimaCitaHasta &&
helper.SoloCitasConsumidas ? t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Consumido) : t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Cancelado) || t.Estado == Convert.ToInt32(EnmEstadoDelTurno.Ausente)
);

Categories