asp.net/C#: Handle datetime if null using LINQ - c#

I have null values in database specifically for Good Expiry Date, which is the field is:good_expiryDate.
What i want is to handle it in this way:
If good_expiryDate is null i want to present it as "" or "-", if it has value it will show the value retrieved from database?
the LINQ query i write is like this, but it return MinValue if good_expiryDate is null(this is what i tried to do).
var query = (from cd in db.CDIndexes
join gds in db.Goods on cd.cdin_CDIndexID equals gds.good_CDIndexId
join itms in db.Items on gds.good_ItemsID equals itms.item_ItemsID
where
cd.cdin_CompanyId==c.Comp_CompanyId&&
(
gds.good_RemainCBM > 0 ||
gds.good_RemainWT > 0 ||
gds.good_RemainPackages > 0 ||
gds.good_RemainVolumeWT > 0
)
&&
itms.item_ItemsID==customerId
select new DataItem
{
depNumber=(int)cd.cdin_Serial,
ItemDesc=gds.good_Name,
gdExpiryDate =(DateTime) gds.good_expiryDate==null?DateTime.MinValue: (DateTime)gds.good_expiryDate,
InvoicBalanceUnits = (decimal) gds.good_RemainPackages,
WTBal=(decimal)gds.good_RemainWT,
VolWT=(decimal)gds.good_RemainVolumeWT
}
);
return query.ToList();
Update: good_expiryDate is type of Datetime, nullable in database

Ensure that gdExpiryDate is of type string.
Then change this line
gdExpiryDate =(DateTime) gds.good_expiryDate==null?DateTime.MinValue: (DateTime)gds.good_expiryDate,
to
gdExpiryDate = gds.good_expiryDate==null? "-" : ((DateTime)gds.good_expiryDate).ToString(),

Change gdExpiryDate type to string type and write like this:
select new DataItem
{
depNumber=(int)cd.cdin_Serial,
ItemDesc=gds.good_Name,
gdExpiryDate = gds.good_expiryDate == null ? "" : gds.good_expiryDate.Value.ToString("dd.mm.yyyy"),
InvoicBalanceUnits = (decimal) gds.good_RemainPackages,
WTBal=(decimal)gds.good_RemainWT,
VolWT=(decimal)gds.good_RemainVolumeWT
}

Related

Null value in the result of a left outer join linq causes error

I have linq query, that left outer join two tables. I found if a value of a field returns null,, then I will get an error message:
"The cast to value type 'System.Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."
I copied my linq below:
var SrvRef = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from r in r_join.DefaultIfEmpty()
where s.svhAccID == accId &&
s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
r.refID
};
bool FBeenPaid = SrvRef.FirstOrDefault().svhBalance == 0M; //this causes error
How can I fix this problem?
I'm slightly surprised at the kind of error you're getting, but there are two places you need to take account of the possibility of the result being null:
Within the query, where r can be null. (If you don't want to match when there are no elements in r_join matching s, you shouldn't be using a left outer join)
In the result itself: you're using FirstOrDefault() which will return null if SrvRef is empty.
So at first glance it should probably be something like:
var query = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from r in r_join.DefaultIfEmpty()
where s.svhAccID == accId && s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
refId = r == null ? 0 : r.refID // Adjust to the appropriate type of refID
};
var result = query.FirstOrDefault();
bool beenPaid = result != null && result.svhBalance == 0m;
With C# 6, you can change the bottom two lines to:
bool beenPaid = query.FirstOrDefault()?.svhBalance == 0m ?? false;
Having said that:
You're not currently using refId in the result anyway - why are you including it in the result?
Are you sure you want a left outer join at all?
Are you sure that taking the first result is really what you want? What if there are multiple results in the join?
Is there any reason you're not doing the whole thing in a query? Something like:
var paid = db.SrvHeads
.Where(s => s.svhAccID == accId && s.svhHeadCnt == HeadId)
.Any(s => db.Refererrants.Any(r => s.svhReferrer == r.refID
&& s.svhBalance == 0m);
.. but just for the precise semantics you want.
I had a similar issue.
Cause: You are using from "r" in r_join.DefaultIfEmpty(). You cannot use same alias name for left outer join.
Solution: Use different alias name if DefaultIfEmpty() cases. Eg: rEmpty
I modified the below query and its working.
var SrvRef = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from rEmpty in r_join.DefaultIfEmpty()
where s.svhAccID == accId &&
s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
refID = rEmpty == null ? 0 : rEmpty.refID
};
what i think is causing an error is that svhBalance is an int32 value type and you are accessing a null value returned by SrvRef.FirstOrDefault().
please try the following line and let me know if it helped you.
if svhBalance is an int value type
var SrvRefObj= SrvRef.FirstOrDefault(); bool FBeenPaid = (((SrvRefObj!=null)&&(SrvRefObj.svhBalance
!=null))?(SrvRefObj.svhBalance == 0):(false))
else if it's a decimal value type
var SrvRefObj= SrvRef.FirstOrDefault(); bool FBeenPaid = (((SrvRefObj!=null)&&(SrvRefObj.svhBalance
!=null))?(SrvRefObj.svhBalance == 0M):(false))

how to add days to datetime by selecting in linq query

I'm trying to select nullable datetime by adding days.
As instance
COMPLETE_TIME = (x.VISIT_DATE.HasValue ? x.VISIT_DATE.Value.AddDays(1) : (DateTime?)null)
If datetime has value i want to add 1 days by selecting query as above.
If datetime does not have value i want to set nullable datetime by selecting query as above.
However if i try above code i get Error (please check bottom side for error)
All Query:
var result=
(from s in context.SURVEYs
join x in context.SURVEY_X on s.SURVEY_ID equals x.SURVEY_ID
join sas in context.SURVEY_ANSWER_SELECTION on s.SURVEY_ID equals sas.SURVEY_ID
join o in context.REP_OPTION on sas.OPTION_ID equals o.OPTION_ID
from PCO in context.REP_PARENT_CHILD_OPTIONS.Where(w => w.CHILD_OPTION_ID == sas.OPTION_ID).DefaultIfEmpty()
where
(s.SURVEY_ID == 5 || s.PARENT_SURVEY_ID == 5) &&
o.SUGGESTION != null &&
PCO.PARENT_OPTION_ID == null
select new
{
SUGGESTION = o.SUGGESTION,
DISPLAY_ORDER = "0",
SUGGESTION_TYPE = o.SUGGESTION_TYPE,
o.EXAMPLE_IMAGE_ID,
COMPLETE_TIME = (x.VISIT_DATE.HasValue ? x.VISIT_DATE.Value.AddDays(1) : (DateTime?)null) // Problem in this part
}).ToList();
Error:
An exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method 'System.DateTime AddDays(Double)' method, and this method cannot be translated into a store expression.
Question:
How can i select added datetime or null datetime in select part ?
Any help will be appreciated.
Thanks.
If you are using EF 6 you could use on of the DbFunctions
var result=
(from s in context.SURVEYs
join x in context.SURVEY_X on s.SURVEY_ID equals x.SURVEY_ID
join sas in context.SURVEY_ANSWER_SELECTION on s.SURVEY_ID equals sas.SURVEY_ID
join o in context.REP_OPTION on sas.OPTION_ID equals o.OPTION_ID
from PCO in context.REP_PARENT_CHILD_OPTIONS.Where(w => w.CHILD_OPTION_ID == sas.OPTION_ID).DefaultIfEmpty()
where
(s.SURVEY_ID == 5 || s.PARENT_SURVEY_ID == 5) &&
o.SUGGESTION != null &&
PCO.PARENT_OPTION_ID == null
select new
{
SUGGESTION = o.SUGGESTION,
DISPLAY_ORDER = "0",
SUGGESTION_TYPE = o.SUGGESTION_TYPE,
o.EXAMPLE_IMAGE_ID,
COMPLETE_TIME = (x.VISIT_DATE.HasValue ? Dbfunctions.AddDays(x.VISIT_DATE.value, 1) : (DateTime?)null) // Problem in this part
}).ToList();

Linq let and if

I am getting:
Non-static method requires a target.
The problem is that Status is null. I don't understand why, because there is a condition which clearly indicates if Status is null return 1.
var filterstatus = (from bq in basequery
let LastStatus = Status == null ? 1
: ((from sd in ems.SampleDatas
where sd.Reference_id == Status.id
&& sd.DateTimeUTC <= bq.DateTimeUTC
orderby sd.DateTimeUTC
select ((sd.Value >= StatusValue) ? 1 : 0)
).DefaultIfEmpty(1).FirstOrDefault())
select new { bq, LastStatus });
It's because it's converting the entire expression into SQL, and not doing the short-circuit in memory (the short circuit would be handled by the database).
You can write something like this, which will properly short-circuit in the database (but still generate the right hand side of the query).
var statusID = Status == null ? (int?)null : Status.id;
var filterstatus = (from bq in basequery
let LastStatus =
statusID == null ?
1 :
((from sd in ems.SampleDatas
where sd.Reference_id == statusID && sd.DateTimeUTC <= bq.DateTimeUTC
orderby sd.DateTimeUTC
select ((sd.Value >= StatusValue) ? 1 : 0)
).DefaultIfEmpty(1).FirstOrDefault())
select new { bq, LastStatus });
Ideally, though, you'd have two separate queries, depending on Status, as it's already known at that point whether the right hand side is required or not.

List.contains throwing null exception?

I have a join query like
public ActionResult Import(int[] userValue, int[] provalue, DateTime? StartDate, DateTime? EndDate)
{
List<int> actallid = db.Activitys.Select(x=>x.ID).ToList();
List<int> actchild = FileOperation.ChildActivity(actallid);
DateTime Start_Date = Convert.ToDateTime(StartDate);
DateTime End_Date = Convert.ToDateTime(EndDate);
IEnumerable<ViewModelActivitySearch> search = from r in db.Reports
join a in db.Activitys on r.ID equals a.Report_ID
join u in db.Users on r.Users_ID equals u.ID
join p in db.Projects on a.Projects_ID equals p.ID
join act in db.ActivityTypes on a.Activity_Type_ID equals act.ID
where (
(r.Start_Date == StartDate || r.End_Date == EndDate || userValue.Contains((int)r.Users_ID) || provalue.Contains((int)a.Projects_ID))
&&
//status is submitted=2, approved=3,exclude child activities
(!actchild.Contains((int)a.ID))
&&
(r.Status == 2 || r.Status == 3)
)
select new ViewModelActivitySearch
{
Id = a.ID,
Activity_Text = a.Activity_Text,
Deliverable = a.Deliverable,
Employee = string.Concat(u.FirstName, " ", u.LastName),
Start_Date = r.Start_Date,
End_Date = r.End_Date,
Activity_Date = a.Activity_Date,
Project = p.Name,
category = act.Activity_Type
};
BindProjectAndUser();
return View(search.ToList());
}
I have a search page where the user can enter a start date,enddate,choose users and choose projects.
I then made a join query to fetch all the data according to that variables.
The problem is when the user doesnot choose any user or project
the code
userValue.Contains((int)r.Users_ID) || provalue.Contains((int)a.Projects_ID))
gives the error
Unable to create a null constant value of type 'System.Int32[]'. Only entity types, enumeration types or primitive types are supported in this context.
What else can I use instead of contains so that this error does not arises.
Thanks
Check for null in values that you try to cast explicitly to int (like r.Users_ID) with ternary operator before using Contains:
where (r.Start_Date == StartDate || r.End_Date == EndDate ||
r.Users_ID != null? userValue.Contains((int)r.Users_ID) : false ||
a.Projects_ID != null ?provalue.Contains((int)a.Projects_ID) : false)
Method Contains() is a method to check if there's a certain value in a list of values of same type and it's perfectly fine to use it here.
Always do server-side validation!
Especially if you're using the users input for a SQL statement it's very important to validate it (sql injection)!
Simplest way: if-statements
if(userinput == null){
return View();
}
Note: If you are using Convert.ToDateTime(), you won't get an exception if the provided DateTime is null. You'll get the DateTime.MinValue and this could end in unexpected results.

Casting String as DateTime in LINQ

I have a table with a following format.
PID ID Label Value
------------------------------------------
1 1 First Name Jenna
1 2 DOB 10/12/1980
I need to retrieve all PIDs where First name starting with J and Month of DOB is 10.
in my code, I retrieve these in DataTable in C# and then tried to use LINQ to retrieve the results I want. This is just an example. These Labels could be anything user defines.
using LINQ I am able to retrieve all PIDs where First Name start with J, but every time I tried to Cast Value for DOB I get cast not valid error. I cannot change the column type in the database since Value could contain any type of information.
Here's a piece of my code. I am new to LINQ, and still trying to figure out around it.
var resultQuery = from r in query.AsEnumerable()
where (r.Field<string>("Label") == Label &&
r.Field<DateTime>("Value").Month == 10)
select r.Field<int>("PID");
Since not all items in the Value column of the table are convertible to DateTime, what you have will fail on invalid conversions. You can add in a clause that first checks that the value is a DateTime and only if it is, converts it and checks the .Month property.
DateTime d;
var resultQuery = from r in query.AsEnumerable()
where (r.Field<string>("Label") == Label &&
DateTime.TryParse(r.Field<string>("Value"), out d) &&
d.Month == 10)
select r.Field<int>("PID");
To potentially improve readability, you could also extract this out into a separate method:
var resultQuery = from r in query.AsEnumerable()
let d = TryGetDate(r.Field<string>("Value"))
where (r.Field<string>("Label") == Label &&
d != null &&
d.Month == 10)
select r.Field<int>("PID");
private DateTime? TryGetDate(string value)
{
DateTime d;
return DateTime.TryParse(value, out d) ? d : default(DateTime?);
}
You are going to end up filtering in memory which isn't very efficient.
So first select your data
var data= from r in query.AsEnumerable();
Then filter on the data
var filtered = from item in data
where item.Label == "Label"
&& Convert.ToDateTime(item.DOB).Month == 10
select item.PID;

Categories