Nullable DateTime in Where Clause in EF Linq Query - linq

Hi I have a table where there is a RespondBy property which is of DateTime and is Nullable. Here is the linq I'm trying to run over EF6:
IEnumerable<Enquiry> ASAPEnquiries = db.Enquiries
.Where(enq => enq.RespondBy == null && enq.JobCostings.Count == 0)
.OrderBy(enq => enq.FlReference);
However, when I run if (ASAPEnquiries.Count() > 0) I get an error stating Nullable object must have a value. How would one query the database using linq if you want to check null DateTime columns?
Thank you.
EDIT:
The SQL that is produced by EF when tested in MSSMS brings back the desired result FYI. SQL Produced:
SELECT
[Project1].[Id] AS [Id],
[Project1].[FlReference] AS [FlReference],
[Project1].[EnquiryDate] AS [EnquiryDate],
[Project1].[ContactName] AS [ContactName],
[Project1].[ProjectReference] AS [ProjectReference],
[Project1].[EnquiryDetails] AS [EnquiryDetails],
[Project1].[RespondBy] AS [RespondBy],
[Project1].[CreatedBy] AS [CreatedBy],
[Project1].[Created] AS [Created],
[Project1].[ModifiedBy] AS [ModifiedBy],
[Project1].[Modified] AS [Modified],
[Project1].[RowVersion] AS [RowVersion],
[Project1].[Enquiry_Customer] AS [Enquiry_Customer],
[Project1].[Enquiry_EnquiryStatus] AS [Enquiry_EnquiryStatus]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[FlReference] AS [FlReference],
[Extent1].[EnquiryDate] AS [EnquiryDate],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[ProjectReference] AS [ProjectReference],
[Extent1].[EnquiryDetails] AS [EnquiryDetails],
[Extent1].[RespondBy] AS [RespondBy],
[Extent1].[CreatedBy] AS [CreatedBy],
[Extent1].[Created] AS [Created],
[Extent1].[ModifiedBy] AS [ModifiedBy],
[Extent1].[Modified] AS [Modified],
[Extent1].[RowVersion] AS [RowVersion],
[Extent1].[Enquiry_Customer] AS [Enquiry_Customer],
[Extent1].[Enquiry_EnquiryStatus] AS [Enquiry_EnquiryStatus],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[JobCostings] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[JobCosting_Enquiry]) AS [C1]
FROM [dbo].[Enquiries] AS [Extent1]
) AS [Project1]
WHERE ([Project1].[RespondBy] IS NULL) AND (0 = [Project1].[C1])
ORDER BY [Project1].[FluidReference] ASC
Also, if I iterate over the collection, I get no error so it seems to be something to do with calling .Count() on the collection.

Use the HasValue property to check if a Nullable has value.
IEnumerable<Enquiry> ASAPEnquiries = db.Enquiries
.Where(enq => enq.RespondBy.HasValue && enq.JobCostings.Count == 0)
.OrderBy(enq => enq.FlReference);

Please make sure that your Enquiry class assign value to the JobCostings property in it's constructor
e.g.
public Enquiry()
{
this.JobCostings = new HashSet<JobCosting>();
//.......
}
and try this one :
IEnumerable<Enquiry> ASAPEnquiries = db.Enquiries
.Where(enq => enq.RespondBy == null && !enq.JobCostings.Any())
.OrderBy(enq => enq.FlReference);

It would seem I have egg on my face. The if statement was calling a void where I was then using item.RespondBy.Value.ToShortDateTime(). How embarresing. Thank you all for your help though, I appreciate your time.

Related

Dynamic LINQ OrderBy performance issue, generating nested select

This is a method used to fetch data for a table. While the code does "work", it has become a bottleneck in our application. In some areas it can take 30 seconds to load a page.
var orderBy = String.IsNullOrEmpty(options.SortColumn) ? GetOrderBy<T>() : options.SortColumn;
var orderbyDirection = (options.SortDirection == SortDirection.Desc ? " descending" : "");
options.TotalSize = queryable.Count();
queryable = queryable
.SearchProperties(options.SearchPhrase)
.OrderBy(orderBy + orderbyDirection)
.SetPaging(options);
return queryable;
On investigation I have found that when it hits the OrderBy statement is for some reason turning the query into a nested select.
an example of a query it would generate
{SELECT
[Project1].[Id] AS [Id],
[Project1].[EntryDate] AS [EntryDate],
[Project1].[Description] AS [Description],
[Project1].[DiaryTypeString] AS [DiaryTypeString],
[Project1].[DiaryViewType] AS [DiaryViewType],
[Project1].[DiaryGroup] AS [DiaryGroup],
[Project1].[ReminderDate] AS [ReminderDate],
[Project1].[HasAttachments] AS [HasAttachments],
[Project1].[OwnerId] AS [OwnerId],
[Project1].[PropertyId] AS [PropertyId],
[Project1].[TenancyId] AS [TenancyId],
[Project1].[SupplierId] AS [SupplierId]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[EntryDate] AS [EntryDate],
[Extent1].[Description] AS [Description],
[Extent1].[DiaryTypeString] AS [DiaryTypeString],
[Extent1].[DiaryViewType] AS [DiaryViewType],
[Extent1].[DiaryGroup] AS [DiaryGroup],
[Extent1].[ReminderDate] AS [ReminderDate],
[Extent1].[HasAttachments] AS [HasAttachments],
[Extent1].[OwnerId] AS [OwnerId],
[Extent1].[PropertyId] AS [PropertyId],
[Extent1].[TenancyId] AS [TenancyId],
[Extent1].[SupplierId] AS [SupplierId]
FROM [dbo].[vwDiary] AS [Extent1]
WHERE (([Extent1].[OwnerId] = #p__linq__0) OR (([Extent1].[OwnerId] IS NULL) AND (#p__linq__0 IS NULL))) AND ( NOT ((N'Work Order' = [Extent1].[DiaryTypeString]) AND ([Extent1].[DiaryTypeString] IS NOT NULL))) AND ( NOT ((N'Alert' = [Extent1].[DiaryTypeString]) AND ([Extent1].[DiaryTypeString] IS NOT NULL)))
) AS [Project1]
ORDER BY [Project1].[EntryDate] DESC}
Im a bit stuck on what to do at this point, is there something wrong with how the OrderBy is implemented? are there any alternative ways to order by a dynamic column?

How can I convert T-SQL with aggregate function to LINQ?

I have a complex script with T-SQL and I couldn't convert it to LINQ syntax:
SELECT cc.ContractID,
Max(CASE WHEN cc.CompanyID = vc.CompanyID THEN vc.CompanyTitle END) AS CompanyID,
Max(CASE WHEN cc.ContractorID = vc.CompanyID THEN vc.CompanyTitle END) AS ContractorID
FROM ConContracts cc
JOIN ViewCompanies vc
ON vc.CompanyID IN ( cc.CompanyID, cc.ContractorID )
GROUP BY cc.ContractID
Can any one help me??
I used Linqer to try and convert this query and ended up with:
from cc in db.ConContracts
from vc in db.ViewCompanies
where
vc.CompanyID == cc.CompanyID ||
vc.CompanyID == cc.ContractorId
group new {cc, vc} by new {
cc.ContractID
} into g
select new {
g.Key.ContractID,
CompanyID = g.Max(p => (p.cc.CompanyID == p.vc.CompanyID ? p.vc.CompanyTitle : null)),
ContractorID = g.Max(p => (p.cc.ContractorId == p.vc.CompanyID ? p.vc.CompanyTitle : null))
}
I'm by no means a LINQ expert, so don't know for sure whether this is a valid answer, but thought it might be helpful or interesting if it is at least functionally satisfactory, as it means you could have used Linqer to answer your question.
This isn't an exact equivalent to your T-SQL, but I suspect it might give you the results you want.
I suspect that ContractID is unique to your ConContracts table and CompanyID is unique to the ViewCompanies table and hence the max is only added because you need it if you are using group by.
So, if your tables are defined correctly you can do something like
(from cc in ConContracts
select new
{
cc.ContractID,
CompanyID = cc.Company.CompanyTitle,
ContractorID = cc.Contractor.CompanyTitle
}

Why is this LINQ to EF query not working?

I have a problem with the following method:
public IQueryable<Customer> ListCustomers(int? parentId)
{
Debug.Assert(parentId == null);
//var list = _customerRepository.List().Where(c => c.ParentId == null);
var list = _customerRepository.List().Where(c => c.ParentId == parentId);
return list;
}
I have one Customer in my DB with a ParentId of null. When I call the method with ListCustomers(null), list is empty for the return statement. If I swap the commented out line in and query with a hard-coded null, then list contains my one customer.
What could cause this difference between these two queries? Why is the one with c.ParentId == parentId not returning anything?
Becouse the Nullable type the linq provider will not generate the proper IS NULL check. See this answer for further information: https://stackoverflow.com/a/785501/1195510
EF translates your query with int? to something like this:
DECLARE #parentId Int = null
SELECT ... WHERE ParentId = #parentId
When this is executed on the database, it doesn't do what you expect because in SQL [column] = NULL is always false.
I agree EF could handle this better, but as a workaround, you can write something like this:
.Where( c => !parentId.HasValue
? !c.ParentId.HasValue
: c.ParentId.Value == parentId.Value
)
EF will then generate a ( somewhat verbose ) SQL statement with the correct IS NULL predicates.
with nullable types you have to use it like this:
.Where(c=> object.Equals(c.ParentId, parentId))

In Operator in Linq

I tried to use the suggestion provided here for using In operator in linq but, i am not able to convert my requirement into LINQ statement.
Below is the SQL query which i need to convert to Linq
select *
from navigator_user_field_property
where user_id = 'albert'
and field_id in (
select field_id
from navigator_entity_field_master
where entity_id = 1
and use_type = 0)
order by field_id
I want this to be converted to a Efficient Linq.
Most of the answers deal with the predetermined list of string array which is not working in my case.
Thanks
Looks like a join to me:
var query = from navigator in db.NavigatorUserFieldProperties
where navigator.UserId == "albert"
join field in db.NavigatorEntityFieldMasters
.Where(f => f.EntityId == 1 && f.UseType == 0)
on navigator.FieldId equals field.FieldId
select navigator;
Note that this will return the same value multiple times if there are multiple fields with the same ID - but I suspect that's not the case.
You could do a more literal translation like this:
var query = from navigator in db.NavigatorUserFieldProperties
where navigator.UserId == "albert" &&
db.NavigatorEntityFieldMasters
.Where(f => f.EntityId == 1 && f.UseType == 0)
.select(f => f.FieldId)
.Contains(navigator.FieldId)
select navigator;
... and that may end up translating to the same SQL... but I'd personally go with the join.
Here is an efficient and readable LINQ query:
var fields =
from field in db.navigator_entity_field_masters
where field.entity_id == 1 && field.user_type == 0
select field;
var properties =
from property in db.navigator_user_field_properties
where property.user_id == "albert"
where fields.Contains(property.field)
select property;
Look mama!! Without joins ;-)

Help required to optimize LINQ query

I am looking to optimize my LINQ query because although it works right, the SQL it generates is convoluted and inefficient...
Basically, I am looking to select customers (as CustomerDisplay objects) who ordered the required product (reqdProdId), and are registered with a credit card number (stored as a row in RegisteredCustomer table with a foreign key CustId)
var q = from cust in db.Customers
join regCust in db.RegisteredCustomers on cust.ID equals regCust.CustId
where cust.CustomerProducts.Any(co => co.ProductID == reqdProdId)
where regCust.CreditCardNumber != null && regCust.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.DisplayName,
RegNumber = cust.RegNumber
};
As an overview, a Customer has a corresponding Person which has the Name; PersonID is a foreign key in Customer table.
If I look at the SQL generated, I see all columns being selected from the Person table. Fyi, DisplayName is an extension method which uses Customer.FirstName and LastName. Any ideas how I can limit the columns from Person?
Secondly, I want to get rid of the Any clause (and use a sub-query) to select all other CustomerIds who have the required ProductID, because it (understandably) generates an Exists clause.
As you may know, LINQ has a known issue with junction tables, so I cannot just do a cust.CustomerProducts.Products.
How can I select all Customers in the junction table with the required ProductID?
Any help/advice is appreciated.
The first step is to start your query from CustomerProducts (as Alex Said):
IQueryable<CustomerDisplay> myCustDisplay =
from custProd in db.CustomerProducts
join regCust in db.RegisteredCustomers
on custProd.Customer.ID equals regCust.CustId
where
custProd.ProductID == reqProdId
&& regCust.CreditCardNumber != null
&& regCust.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
This will simplify your syntax and hopefully result in a better execution plan.
Next, you should consider creating a foreign key relationship between Customers and RegisteredCustomers. This would result in a query that looked like this:
IQueryable<CustomerDisplay> myCustDisplay =
from custProd in db.CustomerProducts
where
custProd.ProductID == reqProdId
&& custProd.Customer.RegisteredCustomer.CreditCardNumber != null
&& custProd.Customer.RegisteredCustomer.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
Finally, for optimum speed, have LINQ compile your query at compile time, rather than run time by using a compiled query:
Func<MyDataContext, SearchParameters, IQueryable<CustomerDisplay>>
GetCustWithProd =
System.Data.Linq.CompiledQuery.Compile(
(MyDataContext db, SearchParameters myParams) =>
from custProd in db.CustomerProducts
where
custProd.ProductID == myParams.reqProdId
&& custProd.Customer.RegisteredCustomer.CreditCardNumber != null
&& custProd.Customer.RegisteredCustomer.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
);
You can call the compiled query like this:
IQueryable<CustomerDisplay> myCustDisplay = GetCustWithProd(db, myParams);
I'd suggest starting your query from the product in question, e.g. something like:
from cp in db.CustomerProducts
join .....
where cp.ProductID == reqdProdID
As you have found, using a property defined as an extension function or in a partial class will require that the entire object is hydrated first and then the select projection is done on the client side because the server has no knowledge of these additional properties. Be glad that your code ran at all. If you were to use the non-mapped value elsewhere in your query (other than in the projection), you would likely see a run-time exception. You can see this if you try to use the Customer.Person.DisplayName property in a Where clause. As you have found, the fix is to do the string concatenation in the projection clause directly.
Lame Duck, I think there is a bug in your code as the cust variable used in your select clause isn't declared elsewhere as a source local variable (in the from clauses).

Resources