How to handle this exception in a cleaner way? - c#

I've been trying to enumerate through a properties of a class and getting their values in a List of strings.
But i faced a problem where i get a NullReferenceException when the value is null from the properties
I Managed to Fix that by that solution, but still i don't see that as a clean code.
I Wonder if this can be implemented in a cleaner and more professional way.
private int CalculateScore()
{
var score = 0;
var answers = new List<string>();
foreach (var prop in typeof(TypesSheet).GetProperties())
{
// WHEN STRING IS MISSING IT BREAKS
try
{
var answer = prop.GetValue(_typesSheet).ToString();
answers.Add(answer);
}
catch
{
continue;
}
}
if (_gameMode == GameMode.SinglePlayer)
{
foreach (var answer in answers)
{
if (string.IsNullOrEmpty(answer))
continue;
score = score + 10;
}
return score;
}
}

Replace:
// WHEN STRING IS MISSING IT BREAKS
try
{
var answer = prop.GetValue(_typesSheet).ToString();
answers.Add(answer);
}
catch
{
continue;
}
With:
var value = prop.GetValue(_typesSheet);
if (null != value)
{
answers.Add(value.toString());
}

if (prop.GetValue(_typesheets) != null) {
answers.Add(prop.GetValue(_typesheets));
}

The error was caused prop.GetValue(_typesSheet) might be null. when you use ToString method will get
nullreferenceexception
You can try to use linq instead of the foreach.
var answers =
typeof(TypesSheet).GetProperties()
.Select(x => x.GetValue(_typesSheet))
.Where(x=> x!= null)
.Select(x=> x.ToString());

Use Safe Navigation Operator (available in C# 6)
var answer = prop.GetValue(_typesSheet)?.ToString();
if (!string.IsNullOrEmpty(answer)) answers.Add(answer);
Linq version
var answers = typeof(TypesSheet)
.GetProperties()
.Select(prop => prop.GetValue(_typesSheet)?.ToString())
.Where(answer => !string.IsNullOrEmpty(answer))
.ToList();

Related

IEnumerable failed to set element

I have a ViewModel that contains different elements inside different tables that I tend to assign to it by query.
My problem is that I can't do this with IEnumerable (in GetAll() below), it keeps returning me null for RoomCode but for a single item (in GetDeviceId() below) then it works fine.
public IEnumerable<DeviceViewModel> GetAll()
{
var result = deviceRepository.GetAll().Select(x => x.ToViewModel<DeviceViewModel>());
for(int i = 0; i < result.Count(); i++)
{
int? deviceID = result.ElementAt(i).DeviceId;
result.ElementAt(i).RoomCode = deviceRepository.GetRoomCode(deviceID);
}
return result;
}
public DeviceViewModel GetDeviceID(int deviceID)
{
var result = new DeviceViewModel();
var device = deviceRepository.Find(deviceID);
if (device != null)
{
result = device.ToViewModel<DeviceViewModel>();
result.RoomCode = deviceRepository.GetRoomCode(deviceID);
}
else
{
throw new BaseException(ErrorMessages.DEVICE_LIST_EMPTY);
}
return result;
}
public string GetRoomCode(int? deviceID)
{
string roomCode;
var roomDevice = dbContext.Set<RoomDevice>().FirstOrDefault(x => x.DeviceId == deviceID && x.IsActive == true);
if (roomDevice != null)
{
var room = dbContext.Set<Room>().Find(roomDevice.RoomId);
roomCode = room.RoomCode;
}
else
{
roomCode = "";
}
return roomCode;
}
First, you need to materialize the query to a collection in local memory. Otherwise, the ElementAt(i) will query the db and give back some kind of temporary object each time it is used, discarding any change you do.
var result = deviceRepository.GetAll()
.Select(x => x.ToViewModel<DeviceViewModel>())
.ToList(); // this will materialize the query to a list in memory
// Now modifications of elements in the result IEnumerable will be persisted.
You can then go on with the rest of the code.
Second (and probably optional), I also recommend for clarity to use foreach to enumerate the elements. That's the C# idiomatic way to loop through an IEnumerable:
foreach (var element in result)
{
int? deviceID = element.DeviceId;
element.RoomCode = deviceRepository.GetRoomCode(deviceID);
}

C# Filter Items In A List According To Multiple Criteria

First, what my situation here is...
My SomeObject has a property string Status which I am interested in for this scenario.
Status property can contain "Open", "Closed", "Finished" values exactly.
I have a method called FilterObjects which returns a List<SomeObject>
Method accepts an argument same as its return type, List<SomeObject>
Method is supposed to filter according to following cases explained below and return the list of objects.
The List<SomeObject> I am sending as argument to my method is guaranteed to be in order (through their ID and type).
The cases are (all related to the string Status property I mentioned):
If any item in the list contains Status = "Finished"; then eliminate all other elements that was in the original list and return only the object that has the "Finished" status.
If any item does NOT contain Status = Finished but contains "CLOSED", I need to check if there is any other item that has the value of "Open" after that "CLOSED" one. You can think of this as a "a task can be closed, but can be reopened. But once it is finished, it cannot be reopened".
If it contains a "CLOSED" and does not have any "OPEN" after that item, I will ignore all the items before CLOSED and only return CLOSED object. If it contains "OPEN" after any closed, I need to return anything AFTER that CLOSED, by excluding itself.
I also tried explain the same thing with my awesome MS Paint skills.
The object itself is not really a problem, but my method is something like this:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
var objects = objectList;
var returnList = new List<SomeObject>();
foreach (var obj in objects)
{
if (obj.Status == "Finished")
{
returnList.Add(obj);
return returnList;
}
}
return new List<SomeObject>();
}
Long story short, what would be the best and most efficient way to apply all this logic in this single method? Honestly, I couldn't go further than the first case I already implemented, which is the FINISHED. Could this whole thing be done with some LINQ magic?
It is guaranteed that I receive an ordered list AND I will never get items more than a couple of hundred so the collection will never be massive.
Many thanks in advance for the help.
You can try something like that:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
SomeObject finished = objectList.FirstOrDefault(o => o.Status.Equals("Finished"));
if (finished != null) { return new List<SomeObject> { finished }; }
List<SomeObject> closed = objectList.SkipWhile(o => !o.Status.Equals("Closed")).ToList();
if (closed.Count == 1) { return closed; }
if (closed.Count > 1) { return closed.Skip(1).ToList(); }
// if you need a new list object than return new List<SomeObject>(objectList);
return objectList;
}
I really wouldn't bother using Linq for this, as you will either create an overly complicated instruction to manage or you will require several loop iterations. I would go for something like this instead:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int lastClosed = -1;
for (int i = 0; i < objectList.Count; i++)
{
if (objectList[i].Status == "Closed")
lastClosed = i;
else if (objectList[i].Status == "Finished")
return new List<SomeObject>() { objectList[i] };
}
if (lastClosed > -1)
if (lastClosed == objectList.Count - 1)
return new List<SomeObject>() { objectList[lastClosed] };
else
return objectList.Skip(lastClosed + 1).ToList();
else
return objectList;
}
EDIT: slightly changed the last bit of code so that it won't trigger an exception if the objectList is empty
LINQ is not well suited and inefficient for scenarios where you need to apply logic based on previous / next elements of a sequence.
The optimal way to apply your logic is to use a single loop and track the Closed status and the position where the status change occurred. At the end you'll return a single element at that position if the last status is Closed, or a range starting at that position otherwise.
static List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int pos = 0;
bool closed = false;
for (int i = 0; i < objectList.Count; i++)
{
var item = objectList[i];
if (item.Status == "Finished")
return new List<SomeObject> { item };
if (item.Status == (closed ? "Opened" : "Closed"))
{
pos = i;
closed = !closed;
}
}
return objectList.GetRange(pos, closed ? 1 : objectList.Count - pos);
}
I did it this way:
public static IEnumerable<SomeObject> convert(this IEnumerable<SomeObject> input)
{
var finished = input.FirstOrDefault(x => x.Status == "Finished");
if (finished != null)
{
return new List<SomeObject> {finished};
}
return input.Aggregate(new List<SomeObject>(), (a, b) =>
{
if (!a.Any())
{
a.Add(b);
}
else if (b.Status == "Open")
{
if (a.Last().Status == "Closed")
{
a.Remove(a.Last());
}
a.Add(b);
}
else if (b.Status == "Closed")
{
a = new List<SomeObject> {b};
}
return a;
});
}
You can write a method like this. This is bare minimum you will have to add null check and exception handling.
public List<SomeCls> GetResult(List<SomeCls> lstData)
{
List<SomeCls> lstResult;
if(lstData.Any(x=>x.Status=="Finished"))
{
lstResult = lstData.Where(x=>x.Status=="Finished").ToList();
}
else if(lstData.Any(x=>x.Status=="Closed"))
{
// Here assuming that there is only one Closed in whole list
int index = lstData.FindIndex(0,lstData.Count(),x=>x.Status=="Closed");
lstResult = lstData.GetRange(index,lstData.Count()-index);
if(lstResult.Count()!=1) // check if it contains Open.
{
lstResult = lstResult.Where(x=>x.Status=="Open").ToList();
}
}
else // Only Open
{
lstResult = lstData;
}
return lstResult;
}
something like this :
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
if (objectList.Where(x => x.Status == "Finished").Any())
{
return objectList.Where(x => x.Status == "Finished").ToList();
}
else if (objectList.Where(x => x.Status == "Closed").Any())
{
if (objectList.FindIndex(x => x.Status == "Closed") == objectList.Count() - 1)
{
return objectList.Where(x => x.Status == "Closed").ToList();
}
else
{
return objectList.GetRange(objectList.FindIndex(x => x.Status == "Closed") + 1, objectList.Count() - (objectList.FindIndex(x => x.Status == "Closed") + 1));
}
}
return objectList;
}

Calling IF Statement method in C#

I have noticed that I am repeating a lot of my code. What I want to do is store the below if-statement in a method so that it can be called at any time. My problem is, I understand methods, slightly, but I'm having difficulty creating a method for what i want. Below is an example of my code. The commented out code is the code i want to put in a method.Any help is much appreciated.
if (result.Equals("") && type == "")
{
JObject jobj = JObject.Parse(Helper.callerClass(#""));
var titleTokens = jobj.SelectTokens("...title");
var values = titleTokens.Select(x => (x as JProperty).Value);
/*if (titleTokens.Contains(s))
{
MessageBox.Show("");
}
else
{
MessageBox.Show("");
}*/
}
private bool Check(string result, string type) {
if (result.Equals("") && type == "")
{
JObject jobj = JObject.Parse(Helper.callerClass(#""));
var titleTokens = jobj.SelectTokens("...title");
var values = titleTokens.Select(x => (x as JProperty).Value);
return titleTokens.Contains(s);
}
return false;
}
You mean like this ?
//To call the function use the following line and replace yourClassName with you class name
yourClassName myInstObj= new yourClassName();
myInstObj.Check(result,type);
//Copied from the above answer b/c it should work fine.
private bool Check(string result, string type) {
if (result.Equals("") && type == "")
{
JObject jobj = JObject.Parse(Helper.callerClass(#""));
var titleTokens = jobj.SelectTokens("...title");
var values = titleTokens.Select(x => (x as JProperty).Value);
if (titleTokens.Contains(s))
{
return true;
}
else
{
return false;
}
}
}

Finding all identifiers containing part of the token

I know I can get a string from resources using
Resources.GetIdentifier(token, "string", ctx.ApplicationContext.PackageName)
(sorry, this is in C#, it's part of a Xamarin.Android project).
I know that if my elements are called foo_1, foo_2, foo_3, then I can iterate and grab the strings using something like
var myList = new List<string>();
for(var i = 0; i < 4; ++i)
{
var id = AppContent.GetIdentifier(token + i.ToString(), "string", "package_name");
if (id != 0)
myList.Add(AppContext.GetString(id));
}
My issue is that my token names all begin with "posn." (the posn can denote the position of anything, so you can have "posn.left_arm" and "posn.brokenose"). I want to be able to add to the list of posn elements, so I can't really store a list of the parts after the period. I can't use a string-array for this either (specific reason means I can't do this).
Is there a way that I can use something akin to "posn.*" in the getidentifer call to return the ids?
You can use some reflection foo to get what you want. It is not pretty at all but it works. The reflection stuff is based on https://gist.github.com/atsushieno/4e66da6e492dfb6c1dd0
private List<string> _stringNames;
private IEnumerable<int> GetIdentifiers(string contains)
{
if (_stringNames == null)
{
var eass = Assembly.GetExecutingAssembly();
Func<Assembly, Type> f = ass =>
ass.GetCustomAttributes(typeof(ResourceDesignerAttribute), true)
.OfType<ResourceDesignerAttribute>()
.Where(ca => ca.IsApplication)
.Select(ca => ass.GetType(ca.FullName))
.FirstOrDefault(ty => ty != null);
var t = f(eass) ??
AppDomain.CurrentDomain.GetAssemblies().Select(ass => f(ass)).FirstOrDefault(ty => ty != null);
if (t != null)
{
var strings = t.GetNestedTypes().FirstOrDefault(n => n.Name == "String");
if (strings != null)
{
var fields = strings.GetFields();
_stringNames = new List<string>();
foreach (var field in fields)
{
_stringNames.Add(field.Name);
}
}
}
}
if (_stringNames != null)
{
var names = _stringNames.Where(s => s.Contains(contains));
foreach (var name in names)
{
yield return Resources.GetIdentifier(name, "string", ComponentName.PackageName);
}
}
}
Then somewhere in your Activity you could do:
var ids = GetIdentifiers("action").ToList();
That will give you all the String Resources, which contain the string action.

C#: Collection was modified; enumeration operation may not execute [duplicate]

This question already has answers here:
How to remove elements from a generic list while iterating over it?
(28 answers)
Closed 9 years ago.
My goal is to delete a user from the user list in my application.But i cannot get to the bottom of this error. Some one plz bail me out.
if (txtEmailID.Text.Length > 0)
{
users = UserRespository.GetUserName(txtEmailID.Text);
bool isUserAvailable=false;
foreach (EduvisionUser aUser in users) // Exception thrown in this line
{
isUserAvailable = true;
if(!aUser.Activated)
{
users.Remove(aUser);
}
}
if (users.Count == 0 && isUserAvailable)
{
DeactivatedUserMessage();
return;
}
}
You can't modify a collection while you're iterating over it with a foreach loop. Typical options:
Use a for loop instead
Create a separate collection of the items you want to act on, then iterate over that.
Example of the second approach:
List<EduvisionUser> usersToRemove = new List<EduvisionUser>();
foreach (EduvisionUser aUser in users) --->***Exception thrown in this line***
{
isUserAvailable = true;
if(!aUser.Activated)
{
usersToRemove.Add(aUser);
}
}
foreach (EduvisionUser userToRemove in usersToRemove)
{
users.Remove(userToRemove);
}
Another alternative, if you're using List<T> is to use List<T>.RemoveAll:
isUserAvailable = users.Count > 0;
users.RemoveAll(user => !user.Activated);
You are trying to delete a user from the list you are looping trough.
this is impossible. Best is to create a new list and add the good ones in it instead of deleting the bad ones
if (txtEmailID.Text.Length > 0)
{
//#new list
List<EduvisionUser> listOfAcceptedUsers = new List<EduvisionUser>()**
users = UserRespository.GetUserName(txtEmailID.Text);
bool isUserAvailable=false;
foreach (EduvisionUser aUser in users) --->***Exception thrown in this line***
{
isUserAvailable = true;
//Add user to list instead of deleting
if(aUser.Activated)
{
ListOfAcceptedUsers.Add(aUser);
}
}
//check new list instead of old one
if (ListOfAcceptedUsers.Count == 0 && isUserAvailable)
{
DeactivatedUserMessage();
return;
}
}
you can do it like this. Use for instead foreach
for( int i =0; i< users.Count; i++ ) --->***Exception thrown in this line***
{
EduvisionUser aUser = users[i];
isUserAvailable = true;
if(!aUser.Activated)
{
users.Remove(aUser);
i--;
}
}
You cannot modify the collection while enumerating. Instead of removing select only what you need and leave the Garbage Collector take care of the rest:
users = users.Where(x => x.Activated);
Or even better, select only what you need from the repository:
users = UserRespository.GetUserName(txtEmailID.Text).Where(x => x.Activated);
My goal is to delete a WorkCalendar from the WorkCalendar but when select Wc that has WorkHour thrown an exception like this:" Collection was modified; enumeration operation may not execute." Any ideas? thanks for the help
Delete method:
try
{
if (!this.DataWorkspace.ApplicationData.WorkCalendars.CanDelete)
{
this.ShowMessageBox("", "", MessageBoxOption.Ok);
return;
}
if (this.WorkCalendars.SelectedItem != null)
{
if ((this.WorkCalendars.SelectedItem.FindCalendarWPs.Count() > 0) || (this.WorkCalendars.SelectedItem.FindCalendarWPs1.Count() > 0))
{
Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke
(() =>
{
RadWindow.Alert(" ");
});
return;
}
var y = DataWorkspace.ApplicationData.WorkCalendarDays.Where(w => w.WorkCalendar.Id == WorkCalendars.SelectedItem.Id).Execute().AsEnumerable();
foreach (var item in y)
{
if(item.WorkingHoursCollection != null && item.WorkingHoursCollection.Count() > 0)
foreach (var WH in item.WorkingHoursCollection)
{
WH.Delete();
}
item.Delete();
}
if (this.WorkCalendars.SelectedItem == this.DataWorkspace.ApplicationData.WorkCalendars.Where(U => U.Id == this.WorkCalendars.SelectedItem.Id).SingleOrDefault())
{
Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke
(() =>
{
RadWindow.Alert(" ");
});
return;
}
this.WorkCalendars.SelectedItem.Delete();
this.Save();
}
}
catch (Exception ex)
{
Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke
(() =>
{
var msg = new LightSwitchApplication.Presentation.GeneralViews.ExceptionMessage();
msg.DataContext = ex;
msg.ShowDialog();
});
}

Categories