C# possible null reference CS8602 - c#

This should be simple but I can't get ride of this null pointer warning. What can you do?
private static List<OrderHeader> orderHeaders = new List<OrderHeader>{...};
/*Delete order line item from the provided OrderHeader*/
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders != null &&
orderHeaders[orderHeaderIndex] != null &&
orderHeaders[orderHeaderIndex].OrderLineItems != null &&
orderHeaders[orderHeaderIndex].OrderLineItems.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}
Here is second attempt.. still not working.
/*Delete order line item from the provided OrderHeader*/
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders is not null &&
orderHeaders[orderHeaderIndex] is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}
Here's the the order Header definition
public class OrderHeader
{
public enum OrderTypes
{
Normal = 0,
Staff,
Mechanical,
Perishable
}
public enum OrderStatusTypes
{
New = 0,
Processing,
Complete
}
[Key]
public string OrderId { get; set; } = string.Empty;
public OrderTypes OrderType { get; set; }
public OrderStatusTypes OrderStatus { get; set; }
public DateTime CreateDate { get; set; } = DateTime.Now;
public string CustomerName { get; set; } = string.Empty;
public List<OrderLine>? OrderLineItems { get; set; }
}
Here is the orderLine definition
public class OrderLine
{
public int LineNumber { get; set; }
public string ProductCode { get; set; } = string.Empty;
public ProductTypes ProductType { get; set; } = 0;
[Column(TypeName = "decimal(18,2)")]
public decimal CostPrice { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal SalePrice { get; set; }
public int Quantity { get; set; }
}

It is a common warning when using <Nullable>enable</Nullable>
Since you are checking that orderHeaders[orderHeaderIndex].OrderLineItems is not null then you can use ! operator to indicate that it cannot be null after that check, so try:
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders is not null &&
orderHeaders[orderHeaderIndex] is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems!.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems!.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}

I'd make life simpler by slightly changing your API and then making the analysis simpler for the compiler:
/*Delete order line item from the provided OrderHeader*/
private bool DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if(orderHeaders is null) return false;
var header = orderHeaders[orderHeaderIndex];
if(header is null) return false;
var lineItems = header.OrderLineItems;
if(lineItems is null || lineItems.Count <= orderLineIndex) return false;
lineItems.RemoveAt(orderLineIndex);
return true;
}
Note, it's now up to the caller to check the return value and report success to the user, log an error, etc1, 2.
This simplifies the compiler's analysis because it doesn't have to reason about, for instance, whether it's possible for the OrderLineItems property to be capable of modifying the orderHeaders collection such that the next time it's indexed into a different result will be returned.
Note also that it doesn't have to reason about that at all anyway since I only index into the collection once and the compiler knows for certain that local variables don't change their nullability after they've been checked.
1I.e. It's a bit of a smell that it's currently the job of this one method both to change a collection and to know the right way to interact with the users.
2In fact I'd actually probably prefer to do an additional range check on orderHeaderIndex and be throwing ArgumentXxxExceptions rather than returning a bool but that might be too much of a change to accept at this stage

Open your .csproj file and locate the sections that has
<Nullable>enable</Nullable>
Update for more information - disabling it will tell the compiler that you not allowing nullable types on a project scale and if there are null you will handle them

Related

Compare Multiple Variables to Same Value Efficiently

I'm looking for alternative methods in C# to compare multiple variables to the same value, and I would optimally like for them to share the same subsequent instructions based on their conditional results.
For example, I have the following code:
string DOBResultsError = VerifySingleRecordReturned(DOBResults, APIParameters, 1);
string NameResultsError = VerifySingleRecordReturned(NameResults, APIParameters, 2);
if (DOBResultsError != string.Empty)
{
PatientRecordUpdate(DOBResults, APIParameters.PatientID, DOBResultsError);
}
else if (NameResultsError != string.Empty)
{
PatientRecordUpdate(NameResults, APIParameters.PatientID, NameResultsError);
}
I'm having to do explicitly instruct PatientRecordUpdate to be performed for each variable being compared to String.Null.
What I would like to have happen is something like the following:
if (DOBResultsError != string.Empty || NameResultsError != string.Empty)
{
//whichever isn't an empty string use to perform PatientRecordUpdate()
}
Is such syntax possible in C#?
Employing the switch keyword won't make a difference because even though I can have multiple circumstances resulting in the same instructions being performed, if I need to use the one of the comparison variables I would still need to explicitly state the code using the variable for each possible case.
string DOBResultsError = VerifySingleRecordReturned(DOBResults, APIParameters, 1);
string NameResultsError = VerifySingleRecordReturned(NameResults, APIParameters, 2);
string SSNResultsError = VerifySingleRecordReturned(SSNResults, APIParameters, 3);
string EmptyString = String.Empty;
switch (EmptyString)
{
case DOBResultsError:
case SSNResultsError: //can't use SSNResultsError with PatientRecordUpdate() without stating PatientRecordUpdate() again
PatientRecordUpdate(DOBResults, APIParameters.PatientID, DOBResultsError);
case NameResultsError:
PatientRecordUpdate(NameResults, APIParameters.PatientID, NameResultsError);
}
Any help appreciated.
UPDATE: Requested additional info
This is what VerifySingleRecordReturnedFrom() does. It checks a few conditions that would cause errors in the program and writes an error message to be added on the record in an SQL DB.
public static string VerifySingleRecordReturnedFrom(List<PatientList3> ReturnedPatientList, AutoPatientLookup APIParameters, int SearchCriteria = 0)
{
string ErrorMessage = String.Empty;
if (ReturnedPatientList.Count == 0)
{
//Error Message for Dob
if (SearchCriteria == 1)
{
ErrorMessage = string.Format("No patients were returned from for DOB ....");
return ErrorMessage;
}
//Error Message for Name
else if (SearchCriteria == 2)
{
ErrorMessage = string.Format("No patients were returned from for patient name ....");
return ErrorMessage;
}
//Error Message for PracticePatientNumber
else if (SearchCriteria == 3)
{
ErrorMessage = string.Format("No patients were returned from for PracticePatientNumber...");
return ErrorMessage;
}
}
// more than one patient in common results list from AttemptToMatchPatientsByDemographics() or results using PatientNumber
else if (ReturnedPatientList.Count() > 1)
{
switch(SearchCriteria)
{
case 1:
case 2:
ErrorMessage = String.Format("{0} number of patients were returned...");
break;
//More than one patient returned from for any given PracticePatientNumber
case 3:
ErrorMessage = String.Format("{0} number of patients were returned....");
break;
}
return ErrorMessage;
}
//No error in number of results from
return ErrorMessage;
}
All of the results(DOB/Name/SSN) types are List objects of the following PatientList3 object (I've included sub classes):
public class PatientList3
{
public Patient PatientNameID { get; set; }
public string PatientNumber { get; set; }
public string ChartNumber { get; set; }
public Gender2 Gender { get; set; }
public string DOB { get; set; }
public string PhoneNumber { get; set; }
public string SSN { get; set; }
}
public class Patient
{
public int ID { get; set; }
public PtName Name { get; set; }
}
public class PtName
{
public string First { get; set; }
public string Middle { get; set; }
public string Last { get; set; }
public string Suffix { get; set; }
public string Full { get; set; }
public string Preferred { get; set; }
}
public class Gender2
{
public string LookupType { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public int Order { get; set; }
public bool Active { get; set; }
public List<AlternateCodes> AlternateCodes { get; set; } //Not important, didn't include AlternativeCodes class
}
This is the class of APIParameters:
public class AutoPatientLookup
{
public string DOB { get; set; }
public string Gender { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int? PatientNumber { get; set; }
public string SSN { get; set; }
public int PracticeID { get; set; }
public int PatientID { get; set; }
}
Consider leveraging a base result type for some generic logic like this:
// assuming both results inherit from or implement some base type...
List<ResultBase> results = new List<ResultBase>()
{
DOBResults,
NameResults
}; // order matters since it's used for the parameter index
// Note that a class would be a much better option than a tuple here.
// Select will verify each result but only upon request until FirstOrDefault is fulfilled
Tuple<ResultBase, string> firstResultError = results
.Select((result, index) => new Tuple<ResultBase, string>(
result,
VerifySingleRecordReturned(result, APIParameters, index + 1)))
.FirstOrDefault(result => !string.IsNullOrEmpty(result.Item2 /* Error message */));
// If there was at least one error, call the original patient update record
// with the associated information.
if (firstResultError != null)
{
PatientRecordUpdate(
firstResultError.Item1 /* Failed result */,
APIParameters.PatientID,
firstResultError.Item2 /* Error message for that result */);
}
You'll want to use a new class instead of a tuple for maintainability reasons, but besides that this should get you started in a good direction.

C# How to initialize an object containing a list<T> with standard values

This question is related to this question. I managed to get one step further, but I am now unable to initialize my whole object with default values in order to prevent it from being null at list level. The goal of this is to hand down the "null" values to my SQL query. Ultimately what I want is one record in my DB that will express: This row has been recorded, but the related values were "null".
I have tried Brian's fiddle and it does not seem to work for me to initialize the whole model with standard values.
Expectation: Upon object initialisation the "null" values should be used and then overwritten in case there is a value coming through JSON deserialisation.
Here is what I have tried. None of this will have the desired effect. I receive this error:
Application_Error: System.ArgumentNullException: Value cannot be null.
Every time I try to access one of the lists in the data model.
namespace Project.MyJob
{
public class JsonModel
{
public JsonModel()
{
Type_X type_x = new Type_X(); // This works fine.
List<Actions> action = new List<Actions>(); // This is never visible
/*in my object either before I initialise JObject or after. So whatever
gets initialised here never makes it to my object. Only Type_X appears
to be working as expected. */
action.Add(new Actions {number = "null", time = "null", station =
"null", unitState = "null"}) // This also does not prevent my
//JsonModel object from being null.
}
public string objectNumber { get; set; }
public string objectFamily { get; set; }
public string objectOrder { get; set; }
public string location { get; set; }
public string place { get; set; }
public string inventionTime { get; set; }
public string lastUpdate { get; set; }
public string condition { get; set; }
public Type_X Type_X { get; set; }
public List<Actions> actions { get; set; }
}
public class Actions
{
public Actions()
{
// None of this seems to play a role at inititialisation.
count = "null";
datetime = "null";
place = "null";
status = "null";
}
// public string count { get; set; } = "null"; should be the same as above
// but also does not do anything.
public string count { get; set; }
public string datetime { get; set; }
public string place { get; set; }
public string status { get; set; }
}
public class Type_X
{
public Type_X
{
partA = "null"; // This works.
}
public string partA { get; set; }
public string PartB { get; set; }
public string partC { get; set; }
public string partD { get; set; }
public string partE { get; set; }
}
}
This is how I now initialize the object based on Brian's answer.
JObject = JsonConvert.DeserializeObject< JsonModel >(json.ToString(), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore});
When I try to iterate over Actions' content, it (logically) gives me above mentioned null error.
for (int i = 0, len = JObject.actions.Count(); i < len; i++)
My current understanding of constructor initialisations:
If I define values such as count = "null"; they should appear in any new object that is created.
If default values are present I would then also expect that a list that has items with default values (such as count for ex.) would be of Count() 1 and not null. How is that even possible?
This will get you out of your bind:
private List<Actions> _actions = new List<Actions>();
public List<Actions> actions { get => _actions; set => _actions = value ?? _actions; }
This causes trying to set actions to null to set it to the previous value, and it is initially not null so it can never be null.
I'm not absolutely sure I'm reading your question right, so here's the same fragment for partA:
private string _partA = "null";
public string partA { get => _partA; set => _partA = value ?? _partA; }
I have found that in some cases, initializing generic lists with their default constructor on your model increases ease of use. Otherwise you will always want to validate they are not null before applying any logic(even something as simple as checking list length). Especially if the entity is being hydrated outside of user code, i.e. database, webapi, etc...
One option is to break up your initialization into two parts. Part 1 being the basic initialization via default constructor, and part 2 being the rest of your hydration logic:
JObject = new List < YourModel >();
... < deserialization code here >
Alternatively you could do this in your deserialization code, but it would add a bit of complexity. Following this approach will allow you to clean up your code in other areas since each access will not need to be immediately proceeded by a null check.

Input string was not in a correct format - Parsing?

I have a page with multiple TextBoxes and ComboBoxes that takes input from user.
Then, I am passing these values forward to save them into my database.
I am encountering error when clicked "Save":
System.FormatException: 'Input string was not in a correct format.'
Here is the code that returns error:
Db_Helper.Insert(new Product(DescriptionBox.Text, int.Parse(QuantityBox.Text), UnitCombo.SelectedItem.ToString(),
int.Parse(RecurrenceTimesCombo.SelectedItem.ToString()), RecurrenceEveryCombo.SelectedItem.ToString(), InterchangeCombo1.SelectedItem.ToString(),
InterchangeCombo2.SelectedItem.ToString(), InterchangeCombo3.SelectedItem.ToString(), PickCatCombo.SelectedItem.ToString(),
PickDietCombo.SelectedItem.ToString()));
Here is the bigger picture:
private async void SaveButton_Click(object sender, RoutedEventArgs e)
{
DatabaseHelperClass Db_Helper = new DatabaseHelperClass();//Creating object for DatabaseHelperClass.cs from ViewModel/DatabaseHelperClass.cs
if (DescriptionBox.Text != "" & QuantityBox.Text != "" & UnitCombo.SelectedItem.ToString() != "" &
RecurrenceTimesCombo.SelectedItem.ToString() != "" & RecurrenceEveryCombo.SelectedItem.ToString() != "" &
PickCatCombo.SelectedItem.ToString() != "" & PickDietCombo.SelectedItem.ToString() != "")
{
Db_Helper.Insert(new Product(DescriptionBox.Text, int.Parse(QuantityBox.Text), UnitCombo.SelectedItem.ToString(),
int.Parse(RecurrenceTimesCombo.SelectedItem.ToString()), RecurrenceEveryCombo.SelectedItem.ToString(), InterchangeCombo1.SelectedItem.ToString(),
InterchangeCombo2.SelectedItem.ToString(), InterchangeCombo3.SelectedItem.ToString(), PickCatCombo.SelectedItem.ToString(),
PickDietCombo.SelectedItem.ToString()));
Frame.Navigate(typeof(ProductsPage));//after adding product redirect to products listbox page
}
else
{
MessageDialog messageDialog = new MessageDialog("Please provide all data needed.");//Text should not be empty
await messageDialog.ShowAsync();
}
}
And here is my Products class:
public class Product
{
[SQLite.Net.Attributes.PrimaryKey, SQLite.Net.Attributes.AutoIncrement]
public int Id { get; set; }
public string CreationDate { get; set; }
public string Name { get; set; }
public int ProductQuantity { get; set; }
public string ProductQuantityUnit { get; set; }
public int RecurrenceTimes { get; set; }
public string RecurrenceEvery { get; set; }
public string Interchangeable1 { get; set; }
public string Interchangeable2 { get; set; }
public string Interchangeable3 { get; set; }
public string Category { get; set; }
public string DietID { get; set; }
public Product(string name, int prodQuantity, string productQuantityUnit,
int recTimes, string recEvery, string interchange1, string interchange2,
string interchange3, string cat, string dietId)
{
CreationDate = DateTime.Now.ToString();
Name = name;
ProductQuantity = prodQuantity;
ProductQuantityUnit = productQuantityUnit;
RecurrenceTimes = recTimes;
RecurrenceEvery = recEvery;
Interchangeable1 = interchange1;
Interchangeable2 = interchange2;
Interchangeable3 = interchange3;
Category = cat;
DietID = dietId;
}
}
What am I doing wrong?
Error does not show up on build. It only shows up when application is already running upon Button Click.
Yes, looks like one of the values you are passing to int.Parse is not an integer after all. But which value causes the problem? (It probably is RecurrenceTimesCombo.SelectedItem.ToString() which is the text of the item, not its numeric value!?)
To make debugging easier, and also to provide helpful feedback to the user, I would recommend doing the parsing in a separate step, and throwing meaningful exceptions if the parsing fails. You can use TryParse instead of Parse, which returns false if the value can not be parsed.
Also, if you want to make sure that the input is not empty, use !string.IsNullOrWhiteSpace(QuantityBox.Text) instead of QuantityBox.Text != "", which does not test for null or a string with only whitespace " ".
int quantityNumber;
if (string.IsNullOrWhiteSpace(QuantityBox.Text)
|| !int.TryParse(QuantityBox.Text, out quantityNumber)) {
throw new ArgumentException(
$"Expected an integer in QuantityBox, but actual value was '{QuantityBox.Text}'.",
nameof(QuantityBox.Text)
);
}
// if we reach this line, quanityNumber has a valid value assigned

allow null/empty value for public datatype

Uploading a file via web form and parsing its contents to a list how do I allow null or empty value when parsing for C.MN,C.LN, C.Val these three are public datatypes declared like this
Namespace datatypes
Public class Uploads
{
Public long Mn {get; set;}
Public int LN { get;set }
Public int Val {Get;Set}
}
List<Uploads> CDU = new List<Uploads>();
string[] fields;
string data = read.ReadLine();
while ((data = read.ReadLine()) != null)
{
if (data.Length != 0)
{
Uploads C = new Uploads();
fields = data.Split(',');
C.LN = Convert.ToInt32(fields[0]);
C.MN = Convert.ToInt64(fields[1]);
C.Val = Convert.ToInt32(fields[2]);
CDU.Add(C);
Simply put, you have to use nullable value types, e.g.
public class Uploads
{
public long? Mn { get; set; }
public int? LN { get; set; }
public int? Val { get; set; }
}
Of course you'll need to work out whether to give them a value or leave them as null, presumably based on whether the string is empty or not.
For example:
C.LN = fields[0] == "" ? (int?) null : Convert.ToInt32(fields[0]);
Or just:
if (fields[0] != "")
{
C.LN = Convert.ToInt32(fields[0]);
}
As an aside, those names are completely unmaintainable. In six months, will you have any idea what they're meant to mean?
Use nullable types like so:
public class Uploads
{
public long? Mn { get; set; }
public int? LN { get; set }
public int? Val { get; set }
}
For information on parsing a string as a nullable value, see here.

foreach hopping out without an exception thrown

This 'foreach' code will quit working and it appears that it's due to some error. However, no exception is thrown. Is there any good reason? The code INSIDE the loop ( ie where the comment is ) never get reached. The failure is while it's enumerating.
foreach (DeviceOption<int> d in _deviceOptions.Where(d => d.HasChanges))
{
//Call some DAL method
}
In case this is part of the equation, this is the 'DeviceOption' class code:
public class DeviceOption
{
private object _state;
public object State
{
get { return _state; }
set
{
if (_state == value)
{
return;
}
HasChanges = true;
_state = value;
}
}
public bool UserEditable { get; set; }
public DateTime Timestamp { get; set; }
public int UserId { get; set; }
public bool HasChanges { get; set; }
public bool IsNew { get; set; }
public Guid ID { get; set; }
public string DisplayCategory { get; set; }
public string Name { get; set; }
}
public class DeviceOption<T> : DeviceOption where T : IComparable
{
private T _value;
public T Value
{
get { return _value; }
set
{
if (value.CompareTo(_value) == 0) { return; }
HasChanges = true;
OriginalValue = _value;
_value = value;
}
}
public T OriginalValue { get; set; }
}`
UPDATE: I figured this out.
I figured this out. Turns out that an invalid cast was happening and the code was faulting. What's odd is that the exception wasn't being thrown. I know this now because I wrapped the code in a try/catch. The behaviour is acting as if this code is running on seperate thread. Is that how LINQ works?
The reason for the cast is because _deviceOptions is a List and contains derived types such as DeviceOption or or etc. By adding this to the LINQ, things are fine now: '&& d is DeviceOption'
Here is the updated code and how I fixed the invalid cast:
try
{
foreach( DeviceOption<int> d in _deviceOptions.Where( d => d.HasChanges && d is DeviceOption<int>) )
{
//blah blah blah
}
}
catch(Exception ex)
{
//this is how I detected the exception. Don't ask why I didn't think of this before :(
Console.Write( ex.Message );
}
It appears that there are no device options where HasChanges is True. In that case the Where extension method yields an empty enumerable.
Well, given the information you have provided, I have to assume that this query returns an empty enumerable:
_deviceOptions.Where(d => d.HasChanges)
Have you used the debugger at all to see what exactly is happening? Pull the query out of the loop expression and see what it contains.

Categories

Resources