Using .Add to add an instance of a class to a generic list is not working.
To illustrate the problem, here are two simple example classes:
public class WorkOrder
{
private List<Note> _Notes;
public List<Note> Notes
{
get
{
return _Notes ?? new List<Note>();
}
set
{
_Notes = value;
}
}
}
public class Note
{
public string NoteText { get; set; }
public System.DateTime Time { get; set; }
public string User { get; set; }
}
You may notice the coding in get on the WorkOrder.Notes property. I put this in so the property wouldn't be initialized with a null value (ref an answer to another question I posted on SO here).
To utilize these classes:
public void Test()
{
WorkOrder tempWorkOrder = new WorkOrder();
Note tempNote = new Note()
{
User = "Aaron",
Time = DateTime.Now,
NoteText = "Work Order pulled from CSV Excel report."
};
tempWorkOrder.Notes.Add(tempNote);
}
I would expect the last line in Test() to add tempNote to the list of Note in tempWorkOrder. However, tempWorkOrder.Notes is null after this line completes. No errors or exceptions are thrown.
I'm using VS2013 Express.
What am I doing wrong?
private List<Note> _Notes;
public List<Note> Notes
{
get
{
return _Notes ?? new List<Note>();
}
set
{
_Notes = value;
}
}
The get is wrong. It should be:
get
{
if (_Notes == null) {
_Notes = new List<Note>();
}
return _Notes;
}
because otherwise you don't save the new List<Note>() you created and every time you use the get you recreate it (the get returns a new List<Note>() but doesn't modify _Notes, so every get checks _Notes, see it's null and return a new List<Note>())
Note that if you hate the world (and your fellow programmers) you can compact the get to:
return _Notes ?? (_Notes = new List<Note>());
(see Ternary/null coalescing operator and assignment expression on the right-hand side?) I don't hate enough the world (and my fellow programmers) to do it :-)
You have not created the list yet there. You need to add a constructor to the WorkOrder as you cannot add to a collection that does not exist. This way, whenever you create a Work Order, you will have an empty list in the `_Notes' field.
It would look something like this:
WorkOrder(){
_Notes = new List<Note>();
}
You never assign _Notes
Do this instead
private List<Note> _Notes;
public List<Note> Notes
{
get
{
if(_Notes == null)
_Notes = new List<Note>();
return _Notes;
}
set
{
_Notes = value;
}
}
You're not initializing _Notes.
So while you get a List<Note> back when _Notes is null, it is not assigning the object to _Notes. Each time you access the public property, it is returning a different List<Note> which is why the Add() call appears to not work.
You should rather use:
get
{
if (_Notes == null)
_Notes = new List<Note>();
return _Notes;
}
It should be possible to use the null-coalescing assignment if you are using C# 8, like so:
get => _Notes ??= new List<Note>();
With brackets:
get
{
return _Notes ??= new List<Note>();
}
In the getter for Notes, you're doing nothing to save a reference to the newly-created list. Therefore, every time you access that getter, you'll get a fresh, empty list. So this:
tempWorkOrder.Notes.Add(tempNote);
...is adding tempNote to a List<Note> that is immediately thrown away.
The problem is your get method:
get
{
return _Notes ?? new List<Note>();
}
Since you don't assign the reference of the object you're creating to _Notes, it keeps being null, and you assigned to a list that isn't referenced anywhere else.
This is what you can do instead:
get
{
if (_Notes == null)
_Notes = new List<Note>();
return _Notes;
}
public class WorkOrder
{
public List<Note> Notes {get;set;}
public WorkOrder()
{
Notes = new List<Note>();
}
}
But in C# 6.0 you should be able to do the following:
public class WorkOrder
{
public List<Note> Notes {get;set;} = new List<Note>();
}
Late to the party, you can create a small extension method which can guard against null or empty list:
public static bool NotNullAndEmpty<T>(this IEnumerable<T> source)
{
if (source != null && source.Any())
return true;
else
return false;
}
Also, if you are using database, then its advisable to use IEnumerable and do all modifications with IEnumerable. Once done, call .ToList() which will result in a single call to the database.
Related
I am trying to retrieve items from TFS to perform autodocumentation of changes and fill the maximum amount possible of information to reduce the time spent doing documentation for our client, like release notes and stuff.
I use the following method to retrieve the items, which is part of a class, both included in the body:
Method:
public IEnumerable<DTO.WorkItem> GetWorkItemsFromQuery(string queryId)
{
using (WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>())
{
var queryResults =
witClient
.QueryByIdAsync(teamProjectName, new Guid(queryId))
.Result;
if (queryResults != null)
{
return queryResults
.WorkItems
.Select(item =>
{
var workitem = witClient.GetWorkItemAsync(item.Id).Result;
workitem.Fields.TryGetValue("DescriptionField", out object descriptionObject);
workitem.Fields.TryGetValue("TitleField", out object titleObject);
return new DTO.WorkItem()
{
Id = item.Id.ToString(),
Title = titleObject?.ToString() ?? string.Empty,
Description = descriptionObject?.ToString() ?? string.Empty
};
}).ToList();
}
return default(IEnumerable<DTO.WorkItem>);
}
}
}
Class:
public class VSTSIssueTrackerExplorer
{
VssConnection connection;
private readonly string teamProjectName;
public VSTSIssueTrackerExplorer(string tfsDefaultCollectionPath,
string personalAccessToken,
string teamProjectName)
{
connection =
new VssConnection(
new Uri(tfsDefaultCollectionPath),
new VssBasicCredential(string.Empty, personalAccessToken));
this.teamProjectName = teamProjectName;
}
public IEnumerable<DTO.WorkItem> GetWorkItemsFromQuery(string queryId)
{
using (WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>())
{
var queryResults =
witClient
.QueryByIdAsync(teamProjectName, new Guid(queryId))
.Result;
if (queryResults != null)
{
return queryResults
.WorkItems
.Select(item =>
{
var workitem = witClient.GetWorkItemAsync(item.Id).Result;
workitem.Fields.TryGetValue("DescriptionField", out object descriptionObject);
workitem.Fields.TryGetValue("TitleField", out object titleObject);
return new DTO.WorkItem()
{
Id = item.Id.ToString(),
Title = titleObject?.ToString() ?? string.Empty,
Description = descriptionObject?.ToString() ?? string.Empty
};
}).ToList();
}
return default(IEnumerable<DTO.WorkItem>);
}
}
}
The first time I call GetWorkItemsFromQuery everything runs fine and retrieves the items, as expected, transforming them with a select to a class WorkItem, shown in the following:
public class WorkItem
{
public string Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
After I've retrieved the items once, if I try to retrieve them again, I get the following exception: System.AggregateException("Cannot Access Disposed Object")
If I remove the using block, I can call it as many times as I need so I don't have an issue with that at all.
What I would like to know is if WorkItemTrackingHttpClient is designed to be disposed when VssConection is itself disposed or I can dispose of it when I no longer need it, I've searched online but it seems I am not capable of finding anything relevant to this matter.
Can anybody shed some light on me if not using the WorkItemTrackingHttpClient inside a using block can be a memory leak?
It seems it's by designed. When you debug the code, you could see when you secondly retrieve the work item, WorkItemTrackingHttpClient is cached in object m_cachedTypes, so you would get exception "Cannot Access Disposed Object".
I don't believe you're supposed to dispose of anything except the VssConnection itself.
Individual clients are never disposed of in Microsoft's sample code, and it appears to be the same pattern as TfsConnection in the old TFS SDK, i.e. there's precedent for only disposing the root object.
I am building xamarin forms app, I am using jamesmontemagno settings plugin and I'm adding a property which list of a class. But the thing is I'm not able to add or insert to the list, every time I use method Add or Insert it jump into the get not the set and I can't understand why.
here is my code and thanks for the help in advance:
//adding an item to the list
private void order(Sales_Order_Items sale)
{
orderlist.Add(sale);
Settings.Usercartlist.Add(sale);
}
I can only set it by using this code
Settings.Usercarlist=orderlist;
the property in the settings file
public static List<Sales_Order_Items> Usercartlist
{
set
{
string listValue = JsonConvert.SerializeObject(value);
AppSettings.AddOrUpdateValue(myIntListKey, listValue);
}
get
{
string value = AppSettings.GetValueOrDefault(myIntListKey, string.Empty);
List<Sales_Order_Items> myList;
if (string.IsNullOrEmpty(value))
myList = new List<Sales_Order_Items>();
else
myList = JsonConvert.DeserializeObject<List<Sales_Order_Items>>(value);
return myList;
}
}
When adding an item, the actual object reference does not change. Only the value changes. This is not picked up by the getter or setter.
The only way to update after each item is to add the line you already have: Settings.Usercarlist=orderlist; after each operation.
I am using like the following way you can try that
Ex.
private static UserDetails _currentUser = null;
public static UserDetails CurrentUser
{
get
{
if (_currentUser == null)
{
string data = AppSettings.GetValueOrDefault(nameof(CurrentUser), string.Empty);
if (data != null)
_currentUser = JsonConvert.DeserializeObject<LoginResponse>(data);
}
return _currentUser;
}
set
{
string data = JsonConvert.SerializeObject(value);
AppSettings.AddOrUpdateValue(nameof(CurrentUser), data);
_currentUser = value;
}
}
This is something that worked up to now and now it just, stopped working (I know, weird, there's probably some silly mistake..)
I have a TripsVM, which contains a list of trips. I load these in my service, returning a List<>.
The problem occurs when I iterate over the trips collection and try to get trip.TripCategory.Name, as the TripCategory is empty, even though TripCategoryID has a value.
This all happens at the backend, I load the trips and then try to iterate over them, they are not being send from the page.
I could probably just load the trip by trip itself, but it used to work and this bug just came up after months of usage.
Any suggestions of where to look for bugs would be really appreciated.
Thanks
Error:
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Where error occurs:
foreach (Trip trip in tripsVM.TripsList) {
var a = trip.TripCategory.Name;
TripsVM:
private List<Trip> _TripsList;
public List<Trip> TripsList
{
get
{
if (_TripsList == null)
{
_TripsList = TripsService.GetTrips();
if (_TripsList == null)
_TripsList = new List<Trip>();
}
return _TripsList;
}
set { _TripsList = value; }
}
Service:
public static List<Trip> GetTrips()
{
return DB.Trips.Where(...).OrderBy(...).ToList();
}
Trip class:
public partial class Trip
{
public int TripID { get; set; }
public int TripCategoryID { get; set; }
....
public virtual TripCategory TripCategory { get; set; }
}
Its looks like your DB context disposed before foreach code or LazyLoadingEnabled set to false in context.
In Service add using
using System.Data.Entity;
And modify loading method
public static List<Trip> GetTrips()
{ return DB.Trips.Where(...).Include(t=>t.TripCategory).OrderBy(...).ToList(); }
I think your code looks fine but you should add some if statements to avoid null exception, because you are returning something with where clause, so you might end up with empty query result and empty list, and in that list you are trying to reach an element of a list object:
if(tripsVM.TripsList != null){
foreach (Trip trip in tripsVM.TripsList) {
var a = trip.TripCategory.Name;
}
}
else
{
// handle empty list
}
private List<Trip> _TripsList;
public List<Trip> TripsList
{
get
{
_TripsList = new List<Trip>();
if(TripsService.GetTrips() != null)
{
_TripsList.add(TripsService.GetTrips());
}
return _TripsList;
}
set { _TripsList = value; }
}
EDIT 2
I have got some help over the past few days on a problem that I am trying to work through. After receiving helpful support from several users, I have come across an error that I have been trying to fix over the weekend and still not succeeded.
I created a Dictionary, where I pass a string Country and also a ICollection of Places for that Country.
Dictionary<string, NewCountryClass> NTCD = new Dictionary<string, NewCountryClass>();
public void AddCountryCollection()
{
newCountryClass = new NewCountryClass(newCountry);
Collections.Add(newCountryClass);
NTCD.Add(newCountryClass.Country, newCountryClass);
}
public void AddPlace()
{
string Country = selectedItem.Country;
RenameQuestion(placeName);
NTCD[Country].Place.Add(placeName);
}
Here is my newCountryClass where I stored the Country and Places in that Country.
private ICollection<string> _places;
public ICollection<string> Places
{
get
{
return _places;
}
set
{
if (value == _places)
return;
_places = value;
RaisePropertyChanged(() => Places);
}
}
This is where the places could be added, but I create an instance of my class at the adding Country stage, and therefore can't pass a place at that time. (EDIT - I have moved the initialising of the collection into the constructor instead of in the Places property, as advised).
public NewCountryClass(string country)
{
_places = new ObservableCollection<string>();
if (country != null)
{
_country = country;
}
}
Therefore, I attempted to create a renamePlace() method:
public void RenamePlace(string place)
{
_places.Add(place);
}
However, _places still seems to be null even with this attempt. Any further ideas or anything I am doing wrong?
You need to learn how to debug a program. Basically, you try to instantiate your NewCountryClass here:
public void AddCountryCollection()
{ // <<< Put breakpoint here <<<
newCountryClass = new NewCountryClass(newCountry, placeName);
Collections.Add(newCountryClass);
NTCD.Add(newCountryClass.Country, newCountryClass);
}
If the placeName input parameter is null in the constructor, then it is also null here... you need to add a breakpoint here and find out why the value is null and ensure that it has a value by this stage in your program.
Would you not be better off initialising the _places collection in the constructor instead of the property get accessor?
public NewCountryClass(string country)
{
_places = new ObservableCollection<string>();
if (country != null)
{
_country = country;
}
}
How do I find and replace a property using Linq in this specific scenario below:
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
//TODO: Just copying values... Find out how to find the index and replace the value
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
Thanks for helping out in advance.
Do not use LINQ because it will not improve the code because LINQ is designed to query collection and not to modify them. I suggest the following.
// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);
if (index != -1)
{
this.Properties[index] = value;
}
else
{
throw new ArgumentOutOfRangeException();
}
Why are Array.Sort() and Array.IndexOf() methods static?
Further I suggest not to use an array. Consider using IDictionary<String, Property>. This simplifies the code to the following.
this.Properties[name] = value;
Note that neither solution is thread safe.
An ad hoc LINQ solution - you see, you should not use it because the whole array will be replaced with a new one.
this.Properties = Enumerable.Union(
this.Properties.Where(p => p.Name != name),
Enumerable.Repeat(value, 1)).
ToArray();
[note: this answer was due to a misunderstanding of the question - see the comments on this answer. Apparently, I'm a little dense :(]
Is your 'Property' a class or a struct?
This test passes for me:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
[TestMethod]
public void TestMethod1()
{
var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
Assert.AreEqual("Y", pb["X"].Value);
pb["X"] = new Property { Name = "X", Value = "Z" };
Assert.AreEqual("Z", pb["X"].Value);
}
I have to wonder why the getter returns a 'Property' instead of whatever datatype .Value, but I'm still curious why you're seeing a different result than what I am.