Linq to SQL check for null strangeness - c#

I have a Linq to SQL query that behaves (in my opinion) very strange when I check for null values.
There is a record in the DB as shown by the last Linq, but why does the 1st two queries not show the record?
//Check
(record.SomeID == null ? "Yes" : "No"); //This prints Yes
//LINQ
var q = (from t in mySQLTable
where t.PKID == record.PKID &&
t.SomeID == record.SomeID
select t).FirstOrDefault();
//This prints nothing. I.e. no records found
var q2 = (from t in mySQLTable
where t.PKID == record.PKID &&
t.SomeID == (record.SomeID == null ? null : record.SomeID)
select t).FirstOrDefault();
//This also prints nothing. I.e. no records found
var q3 = (from t in mySQLTable
where t.PKID == record.PKID &&
t.SomeID == null
select t).FirstOrDefault();
//This prints 1 record

You can overcome this issue using the below query:
bool isNull = record.SomeID == null;
var q = (from t in mySQLTable
where t.PKID == record.PKID
&& ( (isNull && t.SomeID == null)
||
(!isNull && t.SomeID == record.SomeID)
)
select t).FirstOrDefault();

Related

Linq To SQL, why the huge performance difference between two similar queries that are returning the same number of records

I have two queries, the first returns all "Open" records where the reply-by-date has not passed and the second returns all "Open Unviewed" records where the reply-by-date has not passed and the record has not been viewed by the user (no entry in table RfqVieweds). In this scenario the user has not viewed any of the 1000 records so all 1000 are returned. The second to last line in each query (right before "select new RfqDto()") is where the difference is between the two queries.
The first query takes about 45 seconds to return 1000 records. The second query takes about 4 seconds to return the same 1000 records. Why? How do I get the first query to run as fast as the second?
Query 1:
var groupQuery = Rfqs.Where(rfqs => rfqs.IsPrimaryEmail && (rfqs.Contract != "Upstream"))
.GroupBy(rfqs => new {rfqs.RFQ_RFISeqNum})
.Select(g => new {g.Key.RFQ_RFISeqNum, RfqId = g.Max(p => p.RfqId)});
var query = (from m in groupQuery
join t in Rfqs on new {m.RfqId} equals new {t.RfqId}
join s in RfqSupplementals on new {RfqSeqNumber = t.RFQ_RFISeqNum} equals new {s.RfqSeqNumber}
from rfqViewed in RfqVieweds.Where(rv => rv.RfqId == t.RfqId && rv.SalesRep == salesrep).DefaultIfEmpty()
from rfqStarred in RfqUserStarreds.Where(rs => rs.RfqSeqNumber == t.RFQ_RFISeqNum && rs.SalesRep == salesrep).DefaultIfEmpty()
let rcn = RfqCommentNotifications.Where(r => r.RfqSequenceNum == t.RFQ_RFISeqNum && r.SalesRep == salesrep).Select(d => d.LastViewed).FirstOrDefault()
let ch = RfqsChangeHistories.Where(r => r.RfqRfiSeqNum == t.RFQ_RFISeqNum && chgHistList.Contains(r.Action)).OrderByDescending(r => r.Id).FirstOrDefault()
where (isRfqUser == false || ((t.assignedTo == salesrep || t.secndAssignedTo == salesrep || t.addlAssignedTo == salesrep)
|| (agencies.Contains(t.Agency) && (t.assignedTo == null || t.assignedTo.Trim() == string.Empty)
&& (t.secndAssignedTo == null || t.secndAssignedTo.Trim() == string.Empty) && (t.addlAssignedTo == null || t.addlAssignedTo.Trim() == string.Empty))))
&& (!isRfqUser || t.HouseOpportunity == false)
&& t.IsDeleted == false && t.IsPrimaryEmail
&& (t.ReplyByDate > now || (t.ReplyByDate == null && t.IsManualRfq))
select new RfqDto()
Query 2:
var groupQuery = Rfqs.Where(rfqs => rfqs.IsPrimaryEmail && (rfqs.Contract != "Upstream"))
.GroupBy(rfqs => new {rfqs.RFQ_RFISeqNum})
.Select(g => new {g.Key.RFQ_RFISeqNum, RfqId = g.Max(p => p.RfqId)});
var query = (from m in groupQuery
join t in Rfqs on new {m.RfqId} equals new {t.RfqId}
join s in RfqSupplementals on new {RfqSeqNumber = t.RFQ_RFISeqNum} equals new {s.RfqSeqNumber}
from rfqViewed in RfqVieweds.Where(rv => rv.RfqId == t.RfqId && rv.SalesRep == salesrep).DefaultIfEmpty()
from rfqStarred in RfqUserStarreds.Where(rs => rs.RfqSeqNumber == t.RFQ_RFISeqNum && rs.SalesRep == salesrep).DefaultIfEmpty()
let rcn = RfqCommentNotifications.Where(r => r.RfqSequenceNum == t.RFQ_RFISeqNum && r.SalesRep == salesrep).Select(d => d.LastViewed).FirstOrDefault()
let ch = RfqsChangeHistories.Where(r => r.RfqRfiSeqNum == t.RFQ_RFISeqNum && chgHistList.Contains(r.Action)).OrderByDescending(r => r.Id).FirstOrDefault()
where (isRfqUser == false || ((t.assignedTo == salesrep || t.secndAssignedTo == salesrep || t.addlAssignedTo == salesrep)
|| (agencies.Contains(t.Agency) && (t.assignedTo == null || t.assignedTo.Trim() == string.Empty)
&& (t.secndAssignedTo == null || t.secndAssignedTo.Trim() == string.Empty) && (t.addlAssignedTo == null || t.addlAssignedTo.Trim() == string.Empty))))
&& (!isRfqUser || t.HouseOpportunity == false)
&& t.IsDeleted == false && t.IsPrimaryEmail
&& (rfqViewed == null && (t.ReplyByDate > now || (t.ReplyByDate == null && t.IsManualRfq)))
select new RfqDto()
You probably need to look at the query plan to see the true reason for the timing difference, as it depends on the actual data.
The two queries are different and if they happen to return the same number of rows, then Sql will have no way of knowing that in advance.
I can give you an example of one circumstance which could cause the difference.
First of all, lets simplify the query to
var query = (from m in groupQuery
join t in Rfqs on new { m.RfqId } equals new { t.RfqId }
from rfqViewed in RfqVieweds.Where(rv => rv.RfqId == t.RfqId && rv.SalesRep == salesrep).DefaultIfEmpty()
where
t.ReplyByDate > now
select new RfqDto()
Now suppose you have 1 million rows in Rfqs and only 1000 records have a ReplyByDate values in the future. If ReplyByDate is not indexed, then that means that sql has to read and check all million rows before returning the selected 1000 rows.
But if the query becomes
var query = (from m in groupQuery
join t in Rfqs on new { m.RfqId } equals new { t.RfqId }
from rfqViewed in RfqVieweds.Where(rv => rv.RfqId == t.RfqId && rv.SalesRep == salesrep).DefaultIfEmpty()
where
(t.ReplyByDate > now
&& rfqViewed == null
select new RfqDto()
and if it happens that there are only 10000 rows in Rfqs that do not have a record in RfqVieweds, and if rfqViewed has a suitable index that Sql can determine this quickly, then that means that Sql will only need to look at these 10000 records, which may be faster.
Note, If there are any rows with ReplyDates in the future that have been reviewed, then the two queries will not return the same records.

Get the sum of column using Entity Framework

I want to implement the following SQL statement using entity framework:
select coalesce(SUM(cdin_ActMortgageAmnt), 0)
from CRM.dbo.CDIndex,CRM.dbo.company
where comp_companyid = cdin_companyid
and comp_idcust like '%10319%'
and cdin_Deleted is null
and cdin_startunstufdate is not null
and cdin_Status = 'InProgress'
I tried to get the sum of cdin_ActMortgageAmnt:
Company c = db.Companies.Find(750);
var CGP = (from cd in db.CDIndexes
join com in db.Companies on cd.cdin_CompanyId equals com.Comp_CompanyId
where com.Comp_IdCust == c.Comp_IdCust &&
cd.cdin_Deleted == null &&
cd.cdin_startunstufdate == null &&
cd.cdin_Status == "InProgress"
select new
{
act = cd.cdin_ActMortgageAmnt == null ? 0 : cd.cdin_ActMortgageAmnt
}
);
var query = CGP.Sum(x => x.act);
lblSum.Text = query.ToString();
But the query returns null while tracing...

Create a dynamic LINQ Query to cater for different conditions

I have a LINQ query to populate an object (which is then the datasource for a grid) with a few joins.
I want the query to be dynamic so it retrieves rows based on parameters passed, but so far it doesn't work as soon as the StatusID is passed in - it brings back all instances (as if a cartesion product is happening)
_viewfetch.POMastStatusID will either be -1 or a value of 1 or above.
QUERY:
var queryforobject = from p in db.POMasts.AsNoTracking()
join pr in db.Profiles.AsNoTracking() on p.ProfileID equals pr.ID
join c in db.CurrencyTypes.AsNoTracking() on p.CurrencyTypeID equals c.ID
join w in db.WHMasts.AsNoTracking() on p.WarehouseID equals w.ID
join t in db.TermCodeTypes.AsNoTracking() on p.TermCodeTypeID equals t.ID
join s in db.POMastStatusTypes.AsNoTracking() on p.StatusID equals s.ID
//Ensure that these are dynamic
where _viewfetch.VendMastID == -1 || p.VendorID == _viewfetch.VendMastID &&
_viewfetch.POMastStatusID == -1 || p.StatusID == _viewfetch.POMastStatusID
orderby p.ID
//Put the query results into the bespoke object
select new POMastObject { ID = p.ID,
OrderNo = p.OrderNo,
RaisedDate = p.RaisedDate,
RaisedBy = pr.Name,
Currency = c.Description,
Warehouse = w.Description,
Terms = t.Description,
LastEditedBy = p.LastEditedBy,
LastEditedDate = p.LastEditedDate,
Status = s.Name };
if (queryforobject.Count() > 0)
_dataobject = queryforobject.ToList();
Does anyone have any suggestions?
Your query is fine, it's just missing parentheses around the two parts of the && operator:
where (_viewfetch.VendMastID == -1 || p.VendorID == _viewfetch.VendMastID) &&
(_viewfetch.POMastStatusID == -1 || p.StatusID == _viewfetch.POMastStatusID)
Since && has higher precedence than ||, your query effectively evaluates with the two conditions in the middle AND-ed together, like this:
_viewfetch.VendMastID == -1 || (p.VendorID == _viewfetch.VendMastID && _viewfetch.POMastStatusID == -1) || p.StatusID == _viewfetch.POMastStatusID
This is not the logic that you are looking for, because when VendMastID is -1 you get all rows.

How to Select Nullable Value from a coloumn in the List

How can I select the Nullable Value from a column from the list.
Say for example I have a dataset converted into a list like below instead of passing a value in the client id(nullable column). I need to pass null. I've made the following attempts myself:
var reqlist = (from list in tableList.AsEnumerable()
where (list.Field<int?>("ClientID") == clientID)
&& list.Field<bool>("VisibleToAdmin") == true
&& list.Field<bool>("Required") == true
select list.Field<string>("FieldName"));
1.
var reqlist = (from list in tableList.AsEnumerable()
where (list.Field<int?>("ClientID") == null)
&& list.Field<bool>("VisibleToAdmin") == true
&& list.Field<bool>("Required") == true
select list.Field<string>("FieldName"));
2.
var reqlist = (from list in tableList.AsEnumerable()
where (list.Field<int?>("ClientID") == (int?)(null))
&& list.Field<bool>("VisibleToAdmin") == true
&& list.Field<bool>("Required") == true
select list.Field<string>("FieldName"));
3.
var reqlist = (from list in tableList.AsEnumerable()
where (list.Field<int?>("ClientID") == (bool?)(null))
&& list.Field<bool>("VisibleToAdmin") == true
&& list.Field<bool>("Required") == true
select list.Field<string>("FieldName"));
4.
var reqlist = (from list in tableList.AsEnumerable()
where (list.IsNull("ClientID"))
&& list.Field<bool>("VisibleToAdmin") == true
&& list.Field<bool>("Required") == true
select list.Field<string>("FieldName"));
With all of the above methods, an InvalidCastException is thrown.
It's completely legal to compare nullable value with null:
list.Field<int?>("ClientID") == null
// same as
!list.Field<int?>("ClientID").HasValue
Looks like you have DbNull.Value in either VisibleToAdmin or Required field. So, you should use nullable bool to get those fields values:
int? clientID = null;
var reqlist = from r in tableList.AsEnumerable()
where r.Field<int?>("ClientID") == clientID &&
r.Field<bool?>("VisibleToAdmin") == true &&
r.Field<bool?>("Required") == true
select r.Field<string>("FieldName");

How to use try, catch in System.NullReferenceException (Object reference not set to an instance of an object)

I query my data from database to display in my view.
I used this query :
var ien_content = from c in this.DataContext.tbl_Contents
where c.ContentTypeID == id
&&
(
IsActive == false?true :(c.Active == null?true:c.Active > 0)
)
orderby c.RegisterDate descending
select c;
return ien_content.ToList();
There are many rows in this tbl_Contents, but when all of these rows are set Active = 0, it show the error : System.NullReferenceException: Object reference not set to an instance of an object.
Anyone can tell me, how to catch this error? Thanks.
Refine your query with null check for c using where c!= null condition.
Hence you can rewrite it like this:
if(DataContext != null && DataContext.tbl_Contents != null)
{
var ien_content = from c in this.DataContext.tbl_Contents
where c!= null && c.ContentTypeID == id
&&
(
IsActive == false?true :(c.Active == null?true:c.Active > 0)
)
orderby c.RegisterDate descending
select c;
}
If still there is exception then only thing remaining is c.RegisterDate which can be null. So check, if c.Registerdate is not null for any of your rows.
Try replacing the linq with forloop so that you can debug it row by row ,something like this
List list = new List();
foreach(var c in this.DataContext.tbl_Contents)
{
if(c.ContentTypeID == id && ( IsActive == false?true :(c.Active == null?true:c.Active > 0)))
list.Add(c)
}
From the limited information you gave, my guess is that c itself is null. Are you sure there aren't any null rows in tbl_Contents?
First try to test it wihout isActive line:
var ien_content = from c in this.DataContext.tbl_Contents
where c.ContentTypeID == id
orderby c.RegisterDate descending
select c;
if(ien_content.Count() > 0 )
{
//records exist
}
else {//no records}
If there is no problem with that:
var ien_content = from c in this.DataContext.tbl_Contents
where c.ContentTypeID == id
&& (IsActive == false?true :(c.Active == null?true:Convert.ToInt32(c.Active) > 0))
orderby c.RegisterDate descending
select c;
if(ien_content.Count() > 0 )
{
return ien_content.ToList();
}
else {//no records}
Or you can just seperate IsActive on your linq method.
if(IsActive==true)
{
var ien_content = from c in this.DataContext.tbl_Contents
where c.ContentTypeID == id
&& c.Active == null
orderby c.RegisterDate descending
select c;
if(ien_content.Count() > 0 )
{
return ien_content.ToList();
}
else {//no records}
}
else
{
var ien_content = from c in this.DataContext.tbl_Contents
where c.ContentTypeID == id
&& Convert.ToInt32(c.Active) > 0
orderby c.RegisterDate descending
select c;
if(ien_content.Count() > 0 )
{
return ien_content.ToList();
}
else {//no records}
}
Regards

Categories

Resources