Here is an example of the SQL I am trying to convert to Linq
SELECT
* FROM assetassignment
WHERE asgn_type = 'trc' AND asgn_id = '54490' AND lgh_number <> 3015097 AND mov_number <>2030782
and asgn_enddate in (select max(asgn_enddate) FROM assetassignment
WHERE asgn_type = 'trc' AND asgn_id = '54490' AND lgh_number <> 3015097 AND mov_number <> 2030782 and asgn_enddate <= '03/19/2017')
I need to convert this to Linq. I am using a repository as follows:
public async task<AssetAssignment> FindPreviousTrip(string tractorNumber, DateTime endDate, int legNumber,string moveNumber)
{
using (var ctx = new AssetAssignmentContext(_nameOrConnectionString))
{
var previousTrip = from a in ctx.Set<AssetAssignment>()
where a.AssignmentType == "TRC" &&
a.AssignmentId == tractorNumber &&
a.LegId == legNumber &&
a.MoveNumber == moveNumber &&
a.EndDate).in()
}
}
As you can see I am making a stab at it, but keep failing on the subquery for the EndDate in statement.
I'll keep plugging at it, but any help is appreciated.
var previousTrip = from a in ctx.Set<AssetAssignment>()
where a.AssignmentType == "TRC" &&
a.AssignmentId == tractorNumber &&
a.LegId != legNumber &&
a.MoveNumber != moveNumber &&
a.EndDate == (from b in ctx.Set<AssetAssignment>()
where b.AssignmentType == "TRC" && b.AssignmentId == tractorNumber && b.LegId != legNumber && b.MoveNumber != moveNumber && b.EndDate <= new DateTime(2017, 19, 3)
select b.EndDate).Max()
select a;
You may want to create a variable for the common query
var baseTrip = from a in ctx.Set<AssetAssignment>()
where a.AssignmentType == "TRC" &&
a.AssignmentId == tractorNumber &&
a.LegId != legNumber &&
a.MoveNumber != moveNumber
select a;
var previousTrip = from a in baseTrip
where a.EndDate == (from b in baseTrip where b.EndDate <= new DateTime(2017, 19, 3) select b.EndDate).Max()
select a;
Related
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();
I'm using EF 6 with .NET Core. I have written the following query to select some data into a custom object. At the moment the main query returns over 25000 records. Then the filters are applied. This query is running very slow. I'm just trying to find out if I'm doing any mistakes here. Why it could be so slow and is the filtering happening on the memory rather than on the database side?
public IQueryable<TicketViewModel> GetTickets(int companyId, string locale, TicketFilterViewModel filters, Guid customerRef, int? take, int currentUser)
{
IQueryable<TicketViewModel> tickets = (from to in _context.Ticket
join co in _context.Company on to.CompanyId equals co.CompanyId
join con in _context.Contact on to.ContactId equals con.ContactId
join site in _context.Site on to.SiteId equals site.SiteId
join country in _context.Country on site.CountryId equals country.CountryId
join cus in _context.Customer on con.CustomerId equals cus.CustomerId
join tic_type in _context.TicketType on to.TicketTypeId equals tic_type.TicketTypeId
join ts in _context.TicketStatus on to.TicketStatusId equals ts.TicketStatusId
join sb in _context.ServiceBoard on to.ServiceBoardId equals sb.ServiceBoardId into ob1
from a in ob1.DefaultIfEmpty()
join u in _context.User on to.TechnitianId equals u.Id into ob2
from b in ob2.DefaultIfEmpty()
join pr in _context.Priority on to.PriorityId equals pr.PriorityId into ob3
from c in ob3.DefaultIfEmpty()
where to.CompanyId == companyId && (customerRef == Guid.Empty || cus.RefNo == customerRef)
&& to.MergedIntoTicketId == null
select new TicketViewModel
{
CreatedOn = Helpers.Custom.UtcToStandardTime(locale, to.AddedOnUtc).ToString("dd/MM/yyyy hh:mm tt"),
CustomerName = cus.CustomerName,
TicketNumber = to.TicketNumber,
TicketTitle = to.TicketTitle,
RefNo = to.RefNo,
CompanyName = co.CompanyName,
ContactName = string.Concat(con.FirstName, " ", con.LastName),
SiteAddress = String.Concat(site.AddressLine1, ", ", site.AddressLine2, ", ", site.Suburb, ", ", site.State, ", ", site.PostCode, ", ", country.Name),
ServiceBoardName = a.BoardName,
TechnicianName = string.Concat(b.FirstName, " ", b.LastName),
PriorityName = c.PriorityName,
TicketStatus = ts.StatusName, //d.StatusName,
ServiceBoardId = to.ServiceBoardId,
TicketStatusId = to.TicketStatusId,
CustomerId = cus.CustomerId,
PriorityId = to.PriorityId,
ContractId = site.ContractId,
TechnitianId = to.TechnitianId,
TicketId = to.TicketId,
StatusCategoryId = ts.CategoryId,//,//d.CategoryId,
DueOnUtc = to.DueOnUtc,
DefaultStatusId = ts.DefaultId,//d.DefaultId,
ClosedOnUtc = to.ClosedOnUtc,
ResolvedOnUtc = to.ResolvedOnUtc,
InitialResponseMade = to.InitialResponseMade,
TicketTypeId = to.TicketTypeId,
TicketTypeName = tic_type.TicketTypeName
}).OrderByDescending(o => o.TicketNumber);
bool isFiltersHit = false;
if (filters != null)
{
if (tickets != null && tickets.Count() > 0 && filters.serviceboard_selectedItems != null && filters.serviceboard_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.ServiceBoardId != null && filters.serviceboard_selectedItems.Select(o => o.serviceBoardId).Contains(x.ServiceBoardId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.status_selectedItems != null && filters.status_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.TicketStatusId != null && filters.status_selectedItems.Select(o => o.ticketStatusId).Contains(x.TicketStatusId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.type_selectedItems != null && filters.type_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => filters.type_selectedItems.Select(o => o.ticketTypeId).Contains(x.TicketTypeId));
}
if (tickets != null && tickets.Count() > 0 && filters.technician_selectedItems != null && filters.technician_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.TechnitianId != null && filters.technician_selectedItems.Select(o => o.id).Contains(x.TechnitianId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.customer_selectedItems != null && filters.customer_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.CustomerId != 0 && filters.customer_selectedItems.Select(o => o.customerId).Contains(x.CustomerId));
}
if (tickets != null && tickets.Count() > 0 && filters.priority_selectedItems != null && filters.priority_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.PriorityId != null && filters.priority_selectedItems.Select(o => o.priorityId).Contains(x.PriorityId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.contract_selectedItems != null && filters.contract_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.ContractId != null && filters.contract_selectedItems.Select(o => o.contractId).Contains(x.ContractId.Value));
}
if (tickets != null && tickets.Count() > 0 && filters.source_selectedItems != null && filters.source_selectedItems.Count > 0)
{
isFiltersHit = true;
tickets = tickets.Where(x => x.SourceId != 0 && filters.source_selectedItems.Select(o => o.item_id).Contains(x.SourceId));
}
}
if (take.HasValue)
return tickets.Take(take.Value);
return tickets;
}
If you compose the query based on your conditions from the filters, you will see a big improvement. Meaning, create a base query ( base and minimum conditions ) and then add the conditions one by one, depending on what your filters are. Execute the query after all filters are checked.
P.S: you don't really need to call tickets.Count() on every filter. Do it once, in the beginning.
I have a filter called serviceEntryFilter with a property System which could have values for instance EP1, EP2 OR EP1 and sometimes this filter would be null. If there are multiple values or a single value then the query (IN) clause runs fine . If the filter value is null then I get the following error:
Unable to create a constant value of type 'System.String[]'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
[HttpPost]
public ActionResult List(string ServiceEntryStatus, string ServiceEntryReconciled, string ServiceEntryReliabilityRecord, string ActiveServiceEntry,
int PageNo, ServiceEntryFilter serviceEntryFilter = null)
{
string[] systems = null;
var list = (from se in db.ServiceEntry
join r in db.RunLogEntry on se.RunLogEntryID equals r.ID into joinRunLogEntry
from r2 in joinRunLogEntry.DefaultIfEmpty()
join u in db.User on se.TechnicianID equals u.ID
join s in db.System1 on se.SystemID equals s.ID
where (
((se.RunLogEntryID == 0 || se.RunLogEntryID != null))
&& ((serviceEntryFilter.ID.HasValue == false) || (se.ID == serviceEntryFilter.ID.Value && serviceEntryFilter.ID.HasValue == true))
&& ((serviceEntryFilter.ServiceDateTime.HasValue == false) || (EntityFunctions.TruncateTime(se.ServiceDateTime) == EntityFunctions.TruncateTime(serviceEntryFilter.ServiceDateTime) && serviceEntryFilter.ServiceDateTime.HasValue == true))
&& ((serviceEntryFilter.RunDate.HasValue == false) || (EntityFunctions.TruncateTime(r2.RunDate) == EntityFunctions.TruncateTime(serviceEntryFilter.RunDate) && serviceEntryFilter.RunDate.HasValue == true))
&& ((serviceEntryFilter.Technician == null) || (u.FullName.Contains(serviceEntryFilter.Technician.Trim()) && serviceEntryFilter.Technician != null))
&& (
((ServiceEntryStatus == "O" && se.ServiceRequestClosed == false) ||
(ServiceEntryStatus == "C" && se.ServiceRequestClosed == true) ||
(ServiceEntryStatus == "A")
)
)
&& (
((ServiceEntryReliabilityRecord == null) ||
(ServiceEntryReliabilityRecord == "N" && se.ReliabilityRecord == false) ||
(ServiceEntryReliabilityRecord == "Y" && se.ReliabilityRecord == true) ||
(ServiceEntryReliabilityRecord == "A")
)
)
&& (
((ServiceEntryReconciled == null) ||
(ServiceEntryReconciled == "N" && se.Reconciled == false) ||
(ServiceEntryReconciled == "Y" && se.Reconciled == true) ||
(ServiceEntryReconciled == "A")
)
)
&& (
((ActiveServiceEntry == null) ||
(ActiveServiceEntry == "N" && se.Active == false) ||
(ActiveServiceEntry == "Y" && se.Active == true) ||
(ActiveServiceEntry == "A")
)
)
&& (
(s.PlatformID == platformID) || (platformID == 0)
)
&& ((serviceEntryFilter.System == null) || ((serviceEntryFilter.System != null) && systems.Contains(s.SystemFullName)))
)
orderby se.ID descending
select new ServiceSearchEntry()
{
ID = se.ID,
ServiceDateTime = se.ServiceDateTime,
Technician = u.FullName,
System = s.SystemFullName,
ReasonForFailure = se.ReasonForFailure,
RunDate = (r2 == null ? (DateTime?)null : r2.RunDate)
});
var listData = list.Skip((page - 1) * PageSize).Take(PageSize);
ServiceEntriesListViewModel viewModel = new ServiceEntriesListViewModel()
{
ServiceSearchEntry = listData,
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = list.Count()
}
};
}
The Issue:
The following clause is throwing an error when SystemFilter.System is NULL. It is null at times when users do not select values for it. Sample values are as follows:
EP1, EP2
EP1
TP2, TP3, TP4
&& ((serviceEntryFilter.System == null) || ((serviceEntryFilter.System != null) && systems.Contains(s.SystemFullName)))
If it has a value, then I put it in an array and its works like a charm, its just when its null.
The issue is that everything inside a LINQ statement will get translated to SQL. There isn't really a conditional statement that I see here that says "don't try to add this array filter if it's actually null".
I would initialize the systems array to a zero length array, overwrite it if the filter.Systems is not null, and then make my linq statement as follows:
systems.Contains(s.SystemFullName)
Don't include that null checking inside the LINQ statement as it's not doing what you are expecting.
To build conditional LINQ statements, you might want to look at PredicateBuilder: http://www.albahari.com/nutshell/predicatebuilder.aspx
This is a known limitation with Linq to Entities - see section on Referencing Non-Scalar Variables Not Supported.
In otherwords, this line:
systems.Contains(s.SystemFullName)
can't be used as part of your EF query.
The following works fine:
(from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
db.Enquiries.Where(f =>
f.WhereId == e.Id &&
f.WhatId == w.Id &&
f.EnquiryPublished != null &&
f.StatusId != EnquiryMethods.STATUS_INACTIVE &&
f.StatusId != EnquiryMethods.STATUS_REMOVED &&
f.StatusId != EnquiryMethods.STATUS_REJECTED &&
f.StatusId != EnquiryMethods.STATUS_ATTEND
).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});
But:
(from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
EnquiryMethods.BlockOnSite(db.Enquiries.Where(f => f.WhereId == e.Id && f.WhatId == w.Id)).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});
+
public static IQueryable<Enquiry> BlockOnSite(IQueryable<Enquiry> linq)
{
return linq.Where(e =>
e.EnquiryPublished != null &&
e.StatusId != STATUS_INACTIVE &&
e.StatusId != STATUS_REMOVED &&
e.StatusId != STATUS_REJECTED &&
e.StatusId != STATUS_ATTEND
);
}
I get the following error:
base {System.SystemException}: {"Method 'System.Linq.IQueryable1[X.Enquiry]
BlockOnSite(System.Linq.IQueryable1[X.Enquiry])' has no supported translation to SQL."}
Linq to Sql only translates certain method calls to SQL, and yours (BlockOnSite) is not one of them. Hence the error. The fact that your method takes an IQueryable<T> and returns an IQueryable<T> doesn't make it special.
Ok I solved it using:
IQueryable<Enquiry> visibleOnSite = EnquiryMethods.VisibleOnSite(db.Enquiries);
var combinations = (from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
visibleOnSite.Where(f => f.WhereId == e.Id && f.WhatId == w.Id).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});
foreach (var incident in new DataAccess.IncidentRepository().GetItems().Where(
i => (startDate == null || i.IncidentDate >= startDate)
&& (endDate == null || i.IncidentDate <= endDate)
&& (shiftId == null || i.ShiftId == shiftId)
&& (processAreaId == null || i.ProcessAreaId == processAreaId)
&& (plantId == null || i.PlantId == plantId)))
is there a way I can i.PlantId == plantId not to get added if plantId is null?
Thanks
var incident in new DataAccess.IncidentRepository().GetItems().Where(
i => i.IncidentDate >= startDate
&& i.IncidentDate <= endDate
&& i.ShiftId == shiftId
&& i.ProcessAreaId == processAreaId
&& (plantId == null || i.PlantId == plantId)))
Alternatively, you could:
var incidents = new DataAccess.IncidentRepository().GetItems().Where(
i => i.IncidentDate >= startDate
&& i.IncidentDate <= endDate
&& i.ShiftId == shiftId
&& i.ProcessAreaId == processAreaId));
if (plantId != null)
incidents = incidents.Where(i => i.PlantId == plantId);
foreach (var incident in incidents) {
// ...
}
var incident in new DataAccess.IncidentRepository().GetItems().Where(
i => i.IncidentDate >= startDate
&& i.IncidentDate <= endDate
&& i.ShiftId == shiftId
&& i.ProcessAreaId == processAreaId
&& object.Equals(i.PlantId, plantId)))