LINQ Projection AFTER optional parameter filtering - c#

I have this function in a class:
public IEnumerable<PedidosList> Pedidos_Listar(string sComprobante, Clientes MyCliente = null, DateTime? dDesde = null, DateTime? dHasta = null, bool bCumplidos = false)
{
using (var context = new OhmioEntities())
{
IEnumerable<PedidosList> query =
from Pedidos in context.Pedidos
join Clientes in context.Clientes on Pedidos.ID_Cliente equals Clientes.ID_Cliente
where Pedidos.ID_Comprobante == sComprobante
select new PedidosList {ID_Pedido = Pedidos.ID_Pedido, Fecha=Pedidos.Fecha, Aprobado=Pedidos.Aprobado, Bruto=Pedidos.Bruto, Cliente=Clientes.RazonFantasia,
FechaEntrega=Pedidos.FechaEntrega, Neto=Pedidos.Neto, Numero=Pedidos.Numero, Observaciones=Pedidos.Observaciones, Entregado=Pedidos.Entregado, ID_Cliente=Pedidos.ID_Cliente };
if (MyCliente != null) query = query.Where(i => i.ID_Cliente == MyCliente.ID_Cliente);
if (MyCliente != null) query = query.Where(i => i.ID_Cliente == MyCliente.ID_Cliente);
if (dDesde != null && dHasta != null) query = query.Where(i => i.Fecha >= dDesde && i.Fecha <= dHasta);
if (bCumplidos == false) query = query.Where(i => i.Entregado == false);
return query.ToList();
}
}
The idea is to use LINQ projection to fill a custom class object where multiple optional filter parameters are evaluated. My question is: For performance and encapsulation reasons, can I make the projection AFTER optional where filters are applied? In my code the projection is done BEFORE, so i can only filter by the fields on my custom class, but i want to filter over the original class fields. Thank you.

var query =
from Pedidos in context.Pedidos
join Clientes in context.Clientes on Pedidos.ID_Cliente equals Clientes.ID_Cliente
where Pedidos.ID_Comprobante == sComprobante
select new { Pedidos, Clientes };
if (MyCliente != null)
{
query = query.Where(i => i.Pedidos.ID_Cliente == MyCliente.ID_Cliente);
query = query.Where(i => i.Periodos.ID_Cliente == MyCliente.ID_Cliente);
}
if (dDesde != null && dHasta != null)
query = query.Where(i => i.Pedidos.Fecha >= dDesde && i.Pedidos.Fecha <= dHasta);
if (bCumplidos == false)
query = query.Where(i => i.Pedidos.Entregado == false);
return (from x in query
let Pedidos = x.Pedidos
let Clientes = x.Clientes
select new PedidosList {ID_Pedido = Pedidos.ID_Pedido, Fecha=Pedidos.Fecha, Aprobado=Pedidos.Aprobado, Bruto=Pedidos.Bruto, Cliente=Clientes.RazonFantasia, FechaEntrega=Pedidos.FechaEntrega, Neto=Pedidos.Neto, Numero=Pedidos.Numero, Observaciones=Pedidos.Observaciones, Entregado=Pedidos.Entregado, ID_Cliente=Pedidos.ID_Cliente
}).ToList();

Related

42P18: could not determine data type of parameter $12

I'm trying to run this query, but it tells me this error:
42P18: could not determine data type of parameter $12
bds.DataSource = (from vendas in conexao.Vendas
join nota in conexao.NotaFiscal on vendas.IdNotaFIscal equals nota.Id
join clientes in conexao.Cliente on vendas.IdCliente equals clientes.Id
where (nota.DataEmissao >= periodoInicial || periodoInicial == null) &&
(nota.DataEmissao <= periodoFinal || periodoFinal == null) &&
(clientes.Id == idCliente || idCliente == null) &&
(nota.NumeroNotaFiscal >= notaInicial || notaInicial == null) &&
(nota.NumeroNotaFiscal <= notaFinal || notaFinal == null) &&
(nota.Modelo == mod || mod == null)
orderby nota.Id descending
select new
{
Id = nota.Id,
ClienteId = clientes.NomeRazao,
Chave = nota.Chave,
DataEmissao = nota.DataEmissao,
Status = nota.Status,
NumeroNota = nota.NumeroNotaFiscal,
Modelo = nota.Modelo,
}).ToList();
The error occurs in this line below, if I withdraw, works normal, I have tried to change, however I need this filter.I can't understand what I'm doing wrong.
(nota.Modelo == mod || mod == null)
And here's how I fill out the variable
string mod = String.Empty;
if(modelo != "TODOS") mod = modelo == "NF-E" ? "55" : modelo== "NFC-e" ? "65" : null;
Any idea how I can fix it? Thank you.
Try to separate filtering. Such parameters is bad for LINQ Translation and performance. I hope it will solve your problem.
var query =
from vendas in conexao.Vendas
join nota in conexao.NotaFiscal on vendas.IdNotaFIscal equals nota.Id
join clientes in conexao.Cliente on vendas.IdCliente equals clientes.Id
select new { vendas, nota, clientes };
if (periodoInicial != null)
query = query.Where(x => x.nota.DataEmissao >= periodoInicial);
if (periodoFinal != null
query = query.Where(x => x.nota.DataEmissao <= periodoFinal);
if (idCliente != null)
query = query.Where(x => x.clientes.Id == idCliente);
if (notaInicial != null)
query = query.Where(x => x.nota.NumeroNotaFiscal >= notaInicial);
if (notaFinal != null)
query = query.Where(x => x.nota.NumeroNotaFiscal <= notaFinal);
if (mod != null)
query = query.Where(x => x.nota.Modelo == mod);
var result =
from q in query
orderby q.nota.Id descending
select new
{
Id = q.nota.Id,
ClienteId = q.clientes.NomeRazao,
Chave = q.nota.Chave,
DataEmissao = q.nota.DataEmissao,
Status = q.nota.Status,
NumeroNota = q.nota.NumeroNotaFiscal,
Modelo = q.nota.Modelo,
};
bds.DataSource = result.ToList();

Why Same where clause need to write multiple times in a Linq Query for following SQL

What will be the proper linq syntax of below SQL Query ?
select a.id, a.AppointmentStatusID, ad.ID as DetailID
from [dbo].[Appointment] a, [dbo].[AppointmentDetail] ad
where a.[ID] = ad.[AppointmentID]
and a.CompanyID = 'a3dea87a-804e-4115-98cf-472988cf1678'
and a.LocationID = '3165caca-2a48-46f0-bbed-578cff29167t'
and ad.AppDateFrom <= {ts '2017-11-14 23:59:31'}
and ad.AppDateTo >= {ts '2017-11-14 00:00:00'}
and ad.[ApprovalStatusID] = 2
Problem I faced:
I required to filter Where Condition two times 1st at the time within the join & 2nd time during the object.Select expression, please check bellow
var results = (from a in appointments
join ad in _appointmentDetailRepository.GetAll() on a.ID equals ad.AppointmentID
where ad.ApprovalStatusID == 2
&& DbFunctions.TruncateTime(ad.AppDateFrom) <= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
&& DbFunctions.TruncateTime(ad.AppDateTo) >= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
orderby a.ID
select new Appointment
{
ID = a.ID,
CompanyID = a.CompanyID,
LocationID = a.LocationID,
AppointmentDetail = a.AppointmentDetail.Select(ad => new AppointmentDetail
{
ID = ad.ID,
AppDateFrom = ad.AppDateFrom,
AppDateTo = ad.AppDateTo,
AppointmentStatusID = ad.AppointmentStatusID,
}).Where(ad=> ad.ApprovalStatusID == 2
&& DbFunctions.TruncateTime(ad.AppDateFrom) <= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
&& DbFunctions.TruncateTime(ad.AppDateTo) >= DbFunctions.TruncateTime(viewmodel.AppointmentDate)).ToList()
}).GroupBy(x => x.ID).Select(x => x.DefaultIfEmpty().FirstOrDefault());
Query : Why I required to write Where clause 2 times ?
Required Result
An Appointment Object --> Containing ICollection<AppoinmentDetails> if Details.Where Condition == True
From what I see (without knowing the model you have), it looks like you should use the already joined and filtered Details from ad instead of looking it up again from the Property a.AppointmentDetail...
Untested:
select new Appointment
{
ID = a.ID,
CompanyID = a.CompanyID,
LocationID = a.LocationID,
AppointmentDetail = ad.ToList(), // <-- don't you think?
...
}
For the given SQL query
select a.id, a.AppointmentStatusID, ad.ID as DetailID
from [dbo].[Appointment] a, [dbo].[AppointmentDetail] ad
where a.[ID] = ad.[AppointmentID]
and a.CompanyID = 'a3dea87a-804e-4115-98cf-472988cf1678'
and a.LocationID = '3165caca-2a48-46f0-bbed-578cff29167t'
and ad.AppDateFrom <= {ts '2017-11-14 23:59:31'}
and ad.AppDateTo >= {ts '2017-11-14 00:00:00'}
and ad.[ApprovalStatusID] = 2
LINQ query can be written as
var results = (from a in appointments
join ad in appointmentDetails on a.ID equals ad.AppointmentID
where ad.ApprovalStatusID == 2
&& a.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& a.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t"
&& ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
select new
{
ID = a.ID,
AppointmentStatusID = a.AppointmentStatusID,
DetailID = ad.ID
}).ToList();
You can also write it like
var results = appointmentDetails
.Where(ad => ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
&& ad.ApprovalStatusID == 2
&& ad.Appointment.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& ad.Appointment.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t")
.Select(ad =>
new
{
ID = ad.Appointment.ID,
AppointmentStatusID = ad.Appointment.AppointmentStatusID,
DetailID = ad.ID
})
.ToList()
As per the updated question, to get the the Appointment object with a collection of AppointmentDetails, please try this query
var results = appointmentDetails
.Where(ad => ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
&& ad.ApprovalStatusID == 2
&& ad.Appointment.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& ad.Appointment.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t")
.Select(ad =>
new
{
ID = ad.Appointment.ID,
AppointmentStatusID = ad.Appointment.AppointmentStatusID,
Detail = ad
})
.AsEnumerable()
.GroupBy(a => new { a.ID, a.AppointmentStatusID })
.Select(a => new Appointment
{
ID = a.Key.ID,
AppointmentStatusID = a.Key.AppointmentStatusID,
AppointmentDetails = a.Select(d => d.Detail).ToList()
})
.ToList();

linq .Where() issue "Type of condition expression cannot be determined"

I want to be able to pass different variables in a Linq query depending on if a string is null.
string site = null;
int q = a number;
var data = db.tbl_table12345
.Where(site == null
? d => d.stuff_id == org
&& d.Date.Month == q
&& d.Date.Year == year
&& d.Q1 != (int?)null
: d => d.stuff_id == org
&& d.Service == site
&& d.Date.Month == q
&& d.Date.Year == year
&& d.Q1 != (int?)null)
.GroupBy(d => d.Q1)
.Select(d => new
{
q1 = d.Key,
total = d.Count()
});
So in the above example if site == null then we perform a .Where search without the d.Service == site parameter. Else the service parameter is used in addition to the rest of the query. Is this possible?
If you want to add an additional filter to the query when a condition is met then that construct should be outside of the query itself, which LINQ makes quite easy to do:
var query = db.tbl_table12345
.Where(d => d.stuff_id == org
&& d.Date.Month == q
&& d.Date.Year == year
&& d.Q1 != (int?)null);
if (site != null)
query = query.Where(d => d.Service == site);
var data = query.GroupBy(d => d.Q1)
.Select(d => new
{
q1 = d.Key,
total = d.Count()
});
It should work if the ternary operator was "inside" the lambda.
string site = null;
int q = a number;
var data = db.tbl_table12345
.Where(d => site == null
? d.stuff_id == org
&& d.Date.Month == q
&& d.Date.Year == year
&& d.Q1 != (int?)null
: d.stuff_id == org
&& d.Service == site
&& d.Date.Month == q
&& d.Date.Year == year
&& d.Q1 != (int?)null)
.GroupBy(d => d.Q1)
.Select(d => new
{
q1 = d.Key,
total = d.Count()
});
Breaking up the expressions will let you visualize your logic better.
string site = null;
int month = a number;
Expression<Func<SomeType, bool>> nullExpression =
d => d.stuff_id == org
&& SqlFunctions.DatePart("MONTH", d.Date) == month
&& SqlFunctions.DatePart("YEAR", d.Date) == year
&& d.Q1 != (int?)null;
Expression<Func<SomeType, bool>> notNullExpression =
d => d.stuff_id == org
&& SqlFunctions.DatePart("MONTH", d.Date) == month
&& SqlFunctions.DatePart("YEAR", d.Date) == year
&& d.Q1 != (int?)null
&& d.Service == site;
var expression = site == null ? nullExpression : notNullExpression
var data = db.tbl_table12345
.Where(expression)
.GroupBy(d => d.Q1)
.Select(d => new { q1 = d.Key, total = d.Count() });
Or using Expression trees:
var expression = BuildWhere(org, month, year, site);
var data = db.tbl_table12345
.Where(expression)
.GroupBy(d => d.Q1)
.Select(d => new { q1 = d.Key, total = d.Count() });
And here is a method that will build up your where Expression<Func<SomeType, bool>.
public Expression BuildWhere(int org, int month, int year, string service = null)
{
var datePartMethod =
typeof(SqlFunctions)
.GetMethod("DatePart",
new[]
{
typeof(string),
typeof(DateTime?)
});
// Variable d
var variable =
Expression.Variable(typeof(SomeType));
var orgConstant =
Expression.Constant(org);
// d.stuff_id
var stuffId =
Expression.Property(variable, "stuff_id");
// d.stuff_id == org
var stuffIdEquals =
Expression.Equal(stuffId, orgConstant);
// d.Date cast into Nullable DateTime
var date =
Expression.Convert(
Expression.Property(variable, "Date"),
typeof(DateTime?));
var monthPartConstant =
Expression.Constant("MONTH");
// month cast to nullable int
var monthConstant =
Expression.Convert(
Expression.Constant(month),
typeof(int?));
var yearPartConstant =
Expression.Constant("YEAR");
// year cast to nullable int
var yearConstant =
Expression.Convert(
Expression.Constant(year),
typeof(int?));
// SqlFunctions.DatePart("MONTH", d.Date)
var invokeDatePartMonthPart =
Expression.Call(
datePartMethod,
monthPartConstant,
date);
// SqlFunctions.DatePart("YEAR", d.Date)
var invokeDatePartYearPart =
Expression.Call(
datePartMethod,
yearPartConstant,
date);
// SqlFunctions.DatePart("MONTH", d.Date) == month
var dateMonthEquals =
Expression.Equal(
invokeDatePartMonthPart,
monthConstant);
// SqlFunctions.DatePart("MONTH", d.Date) == year
var dateYearEquals =
Expression.Equal(
invokeDatePartYearPart,
yearConstant);
// d.Q1
var q1 = Expression.Property(variable, "Q1");
var nullConstant =
Expression.Constant((int?) null);
// d.Q1 != (int?) null
var q1NotEquals =
Expression.NotEqual(
q1,
nullConstant);
// d.stuff_id == org
// && SqlFunctions.DatePart("MONTH", d.Date) == month
// && SqlFunctions.DatePart("YEAR", d.Date) == year
// && d.Q1 != (int?) null
var andExpression =
Expression.AndAlso(stuffIdEquals,
Expression.AndAlso(dateMonthEquals,
Expression.AndAlso(dateYearEquals,
q1NotEquals)));
// Add d.Service only when not null
if(service != null)
{
// d.Service
var serviceConstant =
Expression.Constant(service);
var serviceProperty =
Expression.Property(
variable,
"Service");
// d.Service == service
var serviceEquals =
Expression.Equal(
serviceProperty,
serviceConstant);
andExpression =
Expression.AndAlso(
andExpression,
serviceEquals);
}
// Creates a lambda to represent the logic
var parameter = Expression.Parameter(typeof(SomeType));
return Expression
.Lambda<Func<SomeType, bool>>(
andExpression,
parameter);
}
From the way your code looks, it appears to me that you're using Entity Framework for your query. If so, you aren't allowed things like d.Date.Month, since EF doesn't know how to properly translate that into SQL by itself. You need to use the SqlFunctions class (specifically the DatePart method) in order to get this query working. Using #Servy's solution as a start:
var query = db.tbl_table12345
.Where(d => d.stuff_id == org
&& SqlFunctions.DatePart("MONTH", d.Date) == q
&& SqlFunctions.DatePart("YEAR", d.Date) == year
&& d.Q1 != (int?)null);
if (site != null)
query = query.Where(d => d.Service == site);
var data = query.GroupBy(d => d.Q1)
.Select(d => new
{
q1 = d.Key,
total = d.Count()
});
Another good reason to use this approach is that all of the LINQ clauses above (Where, GroupBy, and Select) all employ deferred execution (see here for a list of deferred vs. immediate executed methods), which means that only one query will be sent to your database for the final data, and only when you actually use that variable in some way.
Your syntax is wrong
.Where(d => site == null
? d.stuff_id == org
&& d.Date.Month == q && d.Date.Year == year
&& d.Q1 != (int?)null
: d.stuff_id == org
&& d.Service == site && d.Date.Month == q
&& d.Date.Year == year && d.Q1 != (int?)null)

Dynamically Building LINQ-To-Entities Where Clause

How can I build the where clause dynamically, Some time the OwnerID will be zero only itemID and LocationIDwill be provided as the search criteria, in that case the LINQ should be
(from s in repository.ItemOwners.Include("OwnerDetails")
where s.ItemId == searchCriteria.ItemID &&
s.OwnerDetails.LocationId == searchCriteria.LocationID
select new { s.OwnerDetails.OwnerName, s.OwnerDetails.MobNumber }).ToList();
Some time the OwnerID and ItemId will be zero then only the LocationID will be provided as the search criteria, in that case the LINQ should be
(from s in repository.ItemOwners.Include("OwnerDetails")
where s.OwnerDetails.LocationId == searchCriteria.LocationID
select new { s.OwnerDetails.OwnerName, s.OwnerDetails.MobNumber }).ToList();
Some time the whole OwnerID, ItemID and LocationID will be provided as the search criteria, then the LINQ will be like this
(from s in repository.ItemOwners.Include("OwnerDetails")
where s.OwnerId == searchCriteria.OwnerID &&
s.ItemId == searchCriteria.ItemID &&
s.OwnerDetails.LocationId == searchCriteria.LocationID
select new { s.OwnerDetails.OwnerName, s.OwnerDetails.MobNumber }).ToList();
Here only the where clause is changing, Please help me to solve. How I can I build the where clause dynamically (Please note, here I am having a navigation property which is OwnerDetails.LocationId).
You can easily do it by using method-based query. You can add the conditions one at a time and call Select and ToList at the end:
// Where(x => true) might not be necessary, you can try skipping it.
var query = repository.ItemOwners.Include("OwnerDetails").Where(x => true);
if (searchCriteria.OwnerID != null)
query = query.Where(s => s.OwnerID == searchCriteria.OwnerID);
if (searchCriteria.ItemID != null)
query = query.Where(s => s.ItemID == searchCriteria.ItemID);
if (searchCriteria.OwnerID != null)
query = query.Where(s => s..OwnerDetails.LocationId == searchCriteria.LocationID);
var results = query.Select(s => new { s.OwnerDetails.OwnerName, s.OwnerDetails.MobNumber }).ToList();
Simplest is just check the zero condition in the Where clause:
(from s in repository.ItemOwners.Include("OwnerDetails")
where (searchCriteria.OwnerID == 0 || s.OwnerId == searchCriteria.OwnerID) &&
(searchCriteria.ItemID == 0 || s.ItemId == searchCriteria.ItemID) &&
s.OwnerDetails.LocationId == searchCriteria.LocationID
select new { s.OwnerDetails.OwnerName, s.OwnerDetails.MobNumber }).ToList();

How to orderby conditionally using lambda and non lambda linq?

In the below code:
var results = from service
in LogisticsService.GetTransportationModes<CarrierTransportationMode>
(
x => x.CarrierId == carrierRoleId &&
x.ParentTransportationModeId != null &&
!(x is LoadCarrierMode) &&
(x.ParentTransportationMode.TransportationModeStatus != null) &&
x.ParentTransportationMode.TransportationModeStatus.Value != TransportationModeStatus.Inactive
)
//.OrderByDescending(o => o.IsDefault)
//.ThenBy(t => t.ParentTransportationMode.Name)
orderby service.IsDefault descending, service.ParentTransportationMode.Name
select new
{
text = service.ParentTransportationMode.Name,
value = service.ParentTransportationMode.Id
};
If service.IsDefault is null, I need to skip orderby / thenby completely. So the code would execute as if there was no orderby present in the linq like below:
var results = from service
in LogisticsService.GetTransportationModes<CarrierTransportationMode>
(
x => x.CarrierId == carrierRoleId &&
x.ParentTransportationModeId != null &&
!(x is LoadCarrierMode) &&
(x.ParentTransportationMode.TransportationModeStatus != null) &&
x.ParentTransportationMode.TransportationModeStatus.Value != TransportationModeStatus.Inactive
)
select new
{
text = service.ParentTransportationMode.Name,
value = service.ParentTransportationMode.Id
};
I tried to modify the query with conditions like below:
.OrderByDescending(o => o.IsDefault.HasValue ? o.IsDefault : null)
.ThenBy(t => t.IsDefault.HasValue ? t.ParentTransportationMode.Name : null)
orderby !service.IsDefault.HasValue ? null: service.IsDefault descending, service.ParentTransportationMode.Name
But that didn't help.
Do I need to pass any special parameter in the orderby so that the ordering will never happen at all conditionally? any keyword like 'case' or so can be used? If so, how?
Would appreciate any help!.
Thanks!.
As you need to keep the order of the elements with service.IsDefault == null, an easy solution is to split your dataset into two parts (first service.IsDefault == null, second: service.IsDefault != null) sort the second part, then, concat:
var transportationModes = LogisticsService.GetTransportationModes<CarrierTransportationMode>(x =>
x.CarrierId == carrierRoleId &&
x.ParentTransportationModeId != null &&
!(x is LoadCarrierMode) &&
(x.ParentTransportationMode.TransportationModeStatus != null) &&
x.ParentTransportationMode.TransportationModeStatus.Value != TransportationModeStatus.Inactive)
var services = (from service in transportationModes
where service.IsDefault == null
select service).Concat
(from service in transportationModes
where service.IsDefault != null
orderby service.IsDefault descending, service.ParentTransportationMode.Name
select service);
var results = from service in services
select new
{
text = service.ParentTransportationMode.Name,
value = service.ParentTransportationMode.Id
};

Categories