skipping over potential nulls c# - c#

I have this block of code that eventually get serialized to JSON, for use in the Jquery FullCalender plugin. The ToUnixTimeSpan method taskes in a DateTime object and returns the number of seconds since 1970.
DateEnd could be null. In this block of code how do i test for the null and skip the end = ToUnixTimespan(e.DateEnd), if DateEnd is null? is there a C# equivalent to the groovy safe operator?
var listEvents = from e in eventRepository.GetAllEvents()
select new
{
id = e.EventID,
title = e.EventTitle,
start = ToUnixTimespan(e.DateStart),
end = ToUnixTimespan(e.DateEnd),
url = "/Events/Details/" + e.EventID
};
Further info about the ToUnixTimespanMethod:
private long ToUnixTimespan(DateTime date)
{
TimeSpan tspan = date.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0));
return (long)Math.Truncate(tspan.TotalSeconds);
}

Well, how about:
end = e.DateEnd == null ? (long?) null : ToUnixTimespan(e.DateEnd)
It's hard to say for sure as we don't know the type returned by ToUnixTimespan.

Wait-a-minute... Why am I wasting this on a comment, when I could be leveraging Jon's work for some rep.. ;-)
end = e.DateEnd == null ? (long?) null : ToUnixTimespan(e.DateEnd.Value)
That should solve the "cannot convert from 'System.DateTime?' to 'System.DateTime'." error.

Related

Is there a way to compress a check that returns a nullable boolean this into 1-2 lines?

I'm making a simple C# console app and the user has to input either a 1 or a 2 to select their option, and obviously since the user can enter absolutely anything, I need to make a check that returns their input and if it isn't a 1 or a 2, it'll return null.
This is what I made
bool? getResponse = null;
if (read == "1")
{
getResponse = true;
}
else if (read == "2")
{
getResponse = false;
}
else
{
getResponse = null;
}
Knowing C#, there's definitely a way to simplify this but I cant seem to find a way to online. Any pointers?
Probably you are looking for conditional operator ?:.
But this could be complex to maintain and difficult to read if the logic is getting complex (adding logic for read == "3" and etc.).
getResponse = read == "1"
? true
: read == "2"
? false
: null;
Another approach you can apply is switch expression for C# 9.
getResponse = read switch
{
"1" => true,
"2" => false,
_ => null,
};
The third approach is you work around with Dictionary.
using System.Collections.Generic;
using System.Linq;
Dictionary<string, bool> resultDict = new Dictionary<string, bool>
{
{ "1", true },
{ "2", false }
};
getResponse = resultDict.TryGetValue(read, out bool _result)
? _result
: null;
You can use ternary operator in a such situations like this
string read = Console.ReadLine();
bool? response = read == "1" ? true
: read == "2" ? false : null;
but it's best when there is only 2 possible ways, as u can see it's getting out of hand easily. In this situation my code above is ok, but if you have like 10 possibilities maybe something like this is a good approach
// lets say there is ways
// 1 = true, 2 = false, 3 = null
// and any other input means exception
string read = Console.ReadLine()!;
Dictionary<string, bool?> keyValues = new();
keyValues.Add("1", true);
keyValues.Add("2", false);
keyValues.Add("3", null);
bool? response = keyValues.ContainsKey(read) ? keyValues[read]
: throw new Exception();
the exception here is just for example,
my point is that when you have multiple possibilities
doing such thing with dictionary seems much cleaner than if/else, switch/case or multiconditional ternery operator
I consider this pretty unreadable, but it avoids the nested ternary.
getResponse = int.TryParse(read, out var i) && i > 0 && i < 3 ? (bool)(2-i) : null;
Parse the string into an int, make sure it is the valid range, and then do some math so you can cast it to a bool. When casting an integer type to a bool, 0 means false, and non-zero means true.

DateTime substracting days doesn't work

I am using the following code in order to substract a day of the DateTime until I am getting Monday:
DateTime currentWeek = new DateTime(beginDate.Year, beginDate.Month, beginDate.Day);
while (currentWeek.DayOfWeek.ToString() != "Monday")
{
currentWeek.AddDays(-1);
MessageBox.Show(currentWeek.Day.ToString());
MessageBox.Show(currentWeek.DayOfWeek.ToString());
}
beginDate is in the first run set to the current Date of DateTime.Now.
For me this loops forever, and the day of currentWeek always stays the same (29) even though I am substracting 1 everytime I am looping through.
I am already using another function that takes a DateTime and a bool Parameter, which does pretty much the same and works:
private void ErstenTagDerWocheAuswaehlen(DateTime date, bool anfangDerWoche = true)
{
string wochentagName;
int incrementor;
if(anfangDerWoche == true)
{
wochentagName = "Monday";
incrementor = -1;
}
else
{
wochentagName = "Friday";
incrementor = 1;
}
while(date.DayOfWeek.ToString() != wochentagName)
{
date = date.AddDays(incrementor);
}
}
Can someone explain to me why the upper code doesn't work whilst the lower one does?
You have to assign the resulting value, DateTime is immutable.
currentWeek = currentWeek.AddDays(-1);
About your 2nd question:
Use the enum for day of the week, do not try to convert a day of the week to a string for a comparison. The type is DayOfWeek.
Again, a DateTime is not mutable so you have to return a DateTime instance as you can't mutate the one that was passed in (without passing it as ref)
Code change
private DateTime ErstenTagDerWocheAuswaehlen(DateTime date, bool anfangDerWoche = true)
{
System.DayOfWeek wochentagName;
int incrementor;
if(anfangDerWoche == true)
{
wochentagName = System.DayOfWeek.Monday;
incrementor = -1;
}
else
{
wochentagName = System.DayOfWeek.Friday;
incrementor = 1;
}
while(date.DayOfWeek != wochentagName)
{
date = date.AddDays(incrementor);
}
return date;
}
DateTime is an immutable struct, so you need to store the value returned from AddDays():
var t2 = currentWeek.AddDays(-1);
Then use t2. The call to AddDays() doesn't actually change currentWeek.
As DateTime is immutable, when using the AddDays it returns a new DateTime structure with the new information and does not change the given one.
Method documentation states:
Returns a new System.DateTime that adds the specified number of days to the value of this instance.
You must assign it to a variable:
currentWeek = currentWeek.AddDays(-1);

Return default DateTime using Linq Expression

I'm attempting to build out some additional feature for a rules engine that I'm working on using Expression Trees which is based on this post - How to implement a rule engine?
One of the rules I have is to add minutes to a property if that properties type is found to be a DateTime. So for example I could have "DOB + 60" or "DOB - 180".
My current code is working for this:
var expression = new string[] {"DOB", "+", "60"};
var property = Expression.Property(param, expression[0]);
var method = typeof(T).GetProperty(expression[0]).PropertyType.GetMethod("AddMinutes");
/* If we have a AddMinutes method then assume type is of DateTime */
if (method != null)
{
var tParam = method.GetParameters()[0].ParameterType;
/* Calculate if we're adding or subtracting minutes */
var convertedNo = expression[1] == "-" ? int.Parse(expression[2]) * -1 : int.Parse(expression[2]);
var number = Expression.Constant(Convert.ChangeType(convertedNo, tParam));
var exp = Expression.Call(property, method, number);
/* Allows for left property < DOB.AddMinutes(60) */
return Expression.MakeBinary(ExpressionType.LessThan, left, exp);
}
The trouble I'm having is that the DOB property may be set to DateTime.Min so that when my expression comes to evaluate "DOB - 180" in that case an Out of Bounds exception is thrown as it's attempting to subtract 180 mins from DateTime.Min.
I attempted to get round this issue by using an Expression.IfThenElse so that if the DateTime property is equal to DateTime.Min, don't do the calculation and just evaluate on DateTime.Min.
My updated code looks like this:
var expression = new string[] {"DOB", "-", "180"};
var property = Expression.Property(param, expression[0]);
var method = typeof(T).GetProperty(expression[0]).PropertyType.GetMethod("AddMinutes");
/* If we have a AddMinutes method then assume type is of DateTime */
if (method != null)
{
var tParam = method.GetParameters()[0].ParameterType;
/* Calculate if we're adding or subtracting minutes */
var convertedNo = expression[1] == "-" ? int.Parse(expression[2]) * -1 : int.Parse(expression[2]);
var number = Expression.Constant(Convert.ChangeType(convertedNo, tParam));
var exp = Expression.Call(property, method, number);
var minDate = Expression.Constant(Convert.ChangeType(DateTime.MinValue, typeof(DateTime)));
var minDateCheck = Expression.MakeBinary(ExpressionType.Equal, property, minDate);
var minDateExp = Expression.IfThenElse(minDateCheck, minDate, exp);
/* To allow for left property < DOB == DateTime.Min ? DateTime.Min : DOB.AddMinutes(60) */
return Expression.MakeBinary(ExpressionType.LessThan, left, minDateExp); // Exception thrown here
}
Unfortunately this throws the exception - Cannot compare DateTime with return type of void. I've tried several variations but always get the same exception.
Would anyone be able to show me how I could construct an expression that would be able to do this logic?
Expression.IfThenElse is equivalent of the C# - well, if statement, while what you need is the expression equivalent of the C# ?: (a.k.a conditional) operator, which is ConditionalExpression and is produced by Expression.Condition method.
Shortly, replace
var minDateExp = Expression.IfThenElse(minDateCheck, minDate, exp);
with
var minDateExp = Expression.Condition(minDateCheck, minDate, exp);
and you are done.

Using the conditional operator ? to check for null session variable

Take a look at this code:
System.Web.SessionState.HttpSessionState ss = HttpContext.Current.Session["pdfDocument"] ?? false;
if ((Boolean)ss)
{
Label1.Text = (String)Session["docName"];
}
Basically I want to check if HttpContext.Current.Session["pdfDocument"] is not null, and if it isn't to cast to Boolean, then check if its true or false.
I'm trying to avoid nested if statements and figured there would be a more elegant way to do it. I'm therefore only interested in answers that contain the conditional ? operator.
Any tips?
Why do you use ss variable?
What about this:
if (HttpContext.Current.Session["pdfDocument"] != null)
{
Label1.Text = (String)Session["docName"];
}
object ss = HttpContext.Current.Session["pdfDocument"] ?? false;
if ((Boolean)ss)
{
Label1.Text = (String)Session["docName"];
}
Not sure exactly what you're asking for, how about:
System.Web.SessionState.HttpSessionState ss;
Label1.Text = (Boolean)((ss = HttpContext.Current.Session["pdfDocument"]) ?? false) ? (String)Session["docName"] : Label1.Text;
Should leave ss with either a valid session or null, avoids the problem of trying to store false to ss and completely skips the subsequent 'if'. Though there's a repetition of Label1.Text.
Note: this has been edited to take account of the comment by Dave below.
The problem is that you can't do this:
SessionState.HttpSessionState ss = false;
Try putting your nested ifs into an extension method then call that instead.
HttpContext.Current.Session is an System.Web.SessionState.HttpSessionState object, which is a hash or dictionary, as some may call it, of different objects, so unless you're storing an HttpSessionState object as the "pdfDocument" location the first line is incorrect.
If you're actually storing a bool in the "pdfDocument" location which may or may not already be in this slot, you could cast that directly to a bool and null coalesce it: var ss = (bool)(HttpContext.Current.Session["pdfDocument"] ?? false);.
If you're possibly storing some other kind of object in the "pdfDocument" location you could just see if it's currently at that location by checking for null: var ss = HttpContext.Current.Session["pdfDocument"] != null;.
You can try this, though I don't know if it fits your aesthetics:
bool isPdfDocumentSet =
bool.TryParse((HttpContext.Current.Session["pdfDocument"] as string,
out isPdfDocumentSet)
? isPdfDocumentSet
: false;
EDIT: There is actually an even more concise way of doing it:
bool isPdfDocumentSet =
bool.TryParse(HttpContext.Current.Session["pdfDocument"] as string,
out isPdfDocumentSet) && isPdfDocumentSet;
I think the closest you will get to the solution taking that path is following:
System.Web.SessionState.HttpSessionState ss = HttpContext.Current.Session["pdfDocument"];
if (ss != null)
{
Label1.Text = (String)Session["docName"];
}

Checking if Var from LINQ query is Null and returning values older than x

Hello everyone I'm currently having 2 issues with the code below:
Upon return of result1 I'm trying to complete a check to see if it is != null and if it is't it will begin to delete the records selected. The issue is that even when result1 returns nothing and defaults the if statement doesn't pick this up so I guess I'm missing something but what?
I'm wishing to return only values which are over 10 mintues old (this will later be scaled to 12 hours) to do this I'm checking against a.DateTime which is a DateTime value stored in a database. However if i use the <= or >= operators it doesn't work so again what am I missing?
DateTime dateTime = DateTime.Now.Subtract(new TimeSpan(0, 0, 10, 0));
var result1 = (from a in cpuInfo
where a.DateTime <= dateTime
select a).DefaultIfEmpty(null);
if (result1 != null)
{
foreach (TblCPUInfo record1 in result1)
{
localDB.TblCPUInfo.DeleteOnSubmit(record1);
localDB.SubmitChanges();
}
}
Philippe has talked about the sequence side of things - although you don't even need the call to Any(). After all, if there are no changes the loop just won't do anything.
Do you really want to submit the changes on each iteration? It would probably make more sense to do this once at the end. Additionally, you can use DateTime.AddMinutes to make the initial "10 minutes ago" simpler, and if you're only filtering by a Where clause I'd use dot notation.
After all these changes (and making the variable names more useful), the code would look like this:
DateTime tenMinutesAgo = DateTime.Now.AddMinutes(-10);
var entriesToDelete = cpuInfo.Where(entry => entry.DateTime <= tenMinutesAgo);
foreach (var entry in entriesToDelete)
{
localDB.TblCPUInfo.DeleteOnSubmit(entry);
}
localDB.SubmitChanges();
Now, as for why <= isn't working for you... is it possible that you need the UTC time instead of the local time? For example:
DateTime tenMinutesAgo = DateTime.UtcNow.AddMinutes(-10);
If that still isn't working, I suggest you have a look at the generated query and play with it in a SQL tool (e.g. Enterprise Manager or SQL Server Management Studio) to work out why it's not returning any results.
DefaultIfEmpty will return a single item with the content you provided, so in your case a collection with a single value "null".
You should check for elements in the collection using the Any() extension method. In your case:
DateTime dateTime = DateTime.Now.Subtract(new TimeSpan(0, 0, 10, 0));
var result1 = from a in cpuInfo
where a.DateTime <= dateTime
select a;
if (result1.Any())
{
foreach (TblCPUInfo record1 in result1)
{
localDB.TblCPUInfo.DeleteOnSubmit(record1);
localDB.SubmitChanges();
}
}
But if this is really your code, you can skip the Any() check completely, because the foreach loop will not run if there are no elements in result1.

Categories

Resources