I am encountering a rather strange problem. I am iterating over a type of List and within my loop I assign the iterator variable to another local object. Now changing anything in that local object is causing changes in the List on which loop is iterating. Let me clear this with a code sample.
var balances = DBHelperADO.Select("select * from Orders");
// balances is of type List<MyModel>
foreach (var item in balances)
{
MyModel model = new MyModel();
model = item;
var thisQty = details.Where(x => x.Code == item.Code).Sum(x => x.QTY);
// details is another List<MyModel> holding values from the GUI
model.BLNC = model.BLNC - thisQty;
model.VAL = (model.BLNC == 0) ? 0 : model.VAL - (thisQty * model.RATE);
model.TABLE = "Orders";
toUpdate.Add(model); // toUpdate is a List<MyModel>
}
Now my understanding is that the iterator variable (in this case the item) is readonly and when I write:
model = item;
I am making a copy of my item values. But when I do the the calculations on the model object it causes the same changes on the balances list.
I am not getting why its effecting the balances list. I am performing calculations on local scoped model object. Then why those are reflecting on the iterating list (balances).
Please help me what I am doing wrong.
Regards
I assume that the type of the elements in balances is a class type and not a struct.
So what is stored in item and model is a reference to an instance of that class. When you assign model = item; you do not copy the instance, but only the reference to the instance.
When you now access the property of that instance via model.BLNC = ... you change the properties of the original instance. It's the same as calling item.BLNC = ...
The initial line MyModel model = new MyModel() is rather obsolete, as you overwrite the reference to that new instance.
To make a real copy you can try something like
MyModel model = new MyModel
{
BLNC = item.BLNC,
VAL = item.VAL,
TABLE = "Orders"
// ... copy further properties
};
You could also completely rewrite your loop with a LINQ Select statement like this:
var toUpdate = balances.Select(item =>
{
var thisQty = details.Where(x => x.Code == item.Code).Sum(x => x.QTY);
var blnc = item.BLNC - thisQty;
return new MyModel
{
BLNC = blnc,
VAL = (blnc == 0) ? 0 : item.VAL - (thisQty * item.RATE),
RATE = item.RATE,
TABLE = "Orders"
};
}).ToList();
You may have to copy more properties of your MyModel class from item to the new instance. And it may be necessary to use balances.AsEnumerable() if there are problems with nested queries.
model = item; makes a reference, not a copy.
model.BLNC = model.BLNC - thisQty; updats the reference, i.e. it updates the value in the original object.
toUpdate.Add(model); adds the reference back to your original object in the original list to the new list.
Related
In a for loop, instead of declaring a new object and add it to my List, I just update the value of the old object and every time keep adding this old object to my List, why after a few loops all List elements become the same
foreach (vg_ts_VesselCashflow_CashFlow_Entity item in cashflow)
{
var result = new VslMonthlyCashflow_Record();
result.CapitalCost = item.CapitalCost;
result.CharterRevenue = item.CharterRevenue;
result.Date = item.Period;
result.DryDock = item.DryDock;
resultList.Add(result);
}
if (resultList != null)
return resultList;
//Compared with this:
var result = new VslMonthlyCashflow_Record();
foreach (vg_ts_VesselCashflow_CashFlow_Entity item in cashflow)
{
result.CapitalCost = item.CapitalCost;
result.CharterRevenue = item.CharterRevenue;
result.Date = item.Period;
result.DryDock = item.DryDock;
resultList.Add(result);
}
I expect my resultList to be updated but if I keep using the old object, when it loops 123 times, all elements in the List will be the same.
List#Add doesn't copy the object or anything like that, it just keeps a reference to the object you passed to it. In the second snippet, you keep adding the same object to the list multiple times. Each modification you perform on the object is visible through all the references pointing to it, including the local result variable and all the elements of the list.
I'm trying to populate fields in one list with values from another list. I'm having trouble figuring out how to avoid the Argument Out of Range exception in the new List. I tried initializing the size of the new list to myObjectA.Count but then read that this won't actually initialize the list of that size like an array would. I'm a little stuck and was hoping for some assistance. Thanks
List<objectA> myObjectA =_GetList(id);
List<objectB> myObjectB = new List<objectB>();
for (var i=0; i < myObjectA.Count; i++)
{
myObjectB[i].Comments = myObjectA[i].Comments;
}
Because myObjectB is an empty list. You are looping through the myObjectA list which might have one more item and in the first iteration of the loop it will try to execute code like
myObjectB[0].Comments = myObjectA[0].Comments;
Which will crash because there is no items in the myObjectB list and you are trying to access the first item(zeroth index), hence getting the Out of Range exception ! specifically the Index was out of range. Must be non-negative and less than the size of the collection. exception
Assuming both objectB and objectA has Comments property of same type, you can loop through the myObjectA list and for each item, create a new objectB object and add to the list (which was originally initalized as the empty list) using the Add method.
List<objectB> myObjectB = new List<objectB>();
for (var i=0; i < myObjectA.Count; i++)
{
var b = new objectB(); //create the object
b.Comments = myObjectA[i].Comments; // map the property values
myObjectB.Add(b); //add the object to the list
}
The above foreach code can be made to a one liner with LINQ projection
var bList = myObjectA.Select(x => new objectB { Comments = x.Comments }).ToList();
The variable bList will be a list of objectB objects.
You can do like this :
for (var i = 0; i < myObjectA.Count; i++)
{
myObjectB.Add(new objectB()
{
Comments = myObjectA[i].Comments
});
}
This way, with each iteration it'll add new objectB into myObjectB list.
With Linq, you can shorten your code :
myObjectB = myObjectA.Select(x => new objectB { Comments = x.Comments }).ToList();
I have a class that is instantiated at the beginning of each iteration of a loop. Inside the loop, it's attributes need to be populated with the row values of a table returned by a stored procedure. As I have to iterate through each column of every row, in order to know which attribute of the class needs to be assigned a value and when, I have a dictionary that maps the column names to an index. This index refers to a position in a list that stores an attribute of an instance of the class:
while (reader.Read() && reader.HasRows)
{
Subscription subscription = new Subscription();
List<string> subscrData = new List<string>
{
subscription.attr1,
subscription.attr2,
subscription.attr3,
subscription.attr4
}
Dictionary<string, int> columnDict = new Dictionary<string, int>
{
{"attr1": 0},
{"attr2":1},
{"attr3":2},
{"attr4":3}
}
foreach (string colName in columnDict.Keys)
{
if (reader.GetSchemaTable().Columns[colName] == null)
subscrData[columnDict[colName]] = "null";
else
{
subscrData[columnDict[colName]] = reader[colName].ToString();
nullsReturned = false;
}
}
I'm probably coming at this from more of a C++ approach as with that you could store references to the class instance an modify its attributes, but this doesn't work with C# because lists store the values.
How can I restructure this code so that I can modify the actual attributes of the class instance while still being able to check if each column returned from the stored procedure is not null?
You don't need the list for this case. You either want to add a method like setAttribute(string attributeName) to your class (and within it build a switch/case to modify the given attribute); or, use reflection to change an instance field given its name.
I agree with Hasan. But just for your information: to implement your approach you could make use of Lambda expressions to keep track of the references to your properties (= the attributes).
Something like this would work:
Subscription subscription = new Subscription();
List<Expression<Func<Subscription, string>>> subscrData = new List<Expression<Func<Subscription, string>>>
{
a => a.attr1,
a => a.attr2,
a => a.attr3,
a => a.attr4,
};
//E.g. To update attribute 3 you can do this:
var prop = (PropertyInfo)((MemberExpression)subscrData[2].Body).Member;
prop.SetValue(subscription, "test string", null);
I am querying azure table. After getting the data I am performing linq select operation and getting modified values.
But I want two lists one with old values and other with new values.
var oldUserEntities = userEntities.ToList();
var newUserEntities = userEntities.Select(i => { i.RowKey = dict[i.RowKey]; return i; }).ToList();
After this code if I verify values in oldUserEntites and newUserEntities, both having same modified values.
How to have old list and new list?
That's because the i in your projection is referencing the original item in oldUserEntities, then i.RowKey is modifying the original data.
Try this instead (assuming your entity is named UserEntity):
var oldUserEntities = userEntities.ToList();
var newUserEntities = userEntities.Select(i => new UserEntity
{
RowKey = dict[i.RowKey],
// rest of desired properties ...
}).ToList();
I'm not really sure what you're trying to do here, but this
> i => { i.RowKey = dict[i.RowKey]; return i }
is changing RowKey on every object in the list. The "return i" is then making a list containing the same, now modified, objects.
all this is really doing is
foreach(i in userEntities)
i.RowKey = dict[i.RowKey]
and then making a copy of the list
So I'm just starting out with ASP.NET MVC, and I've run into an issue I just can't find an answer to.
I'm grabbing some data from an SQL database, and send it to the view with this code:
var listingsQuery = (from s in db.tblListings select s).OrderBy(s => s.listingID).Skip(100).Take(25);
var listings = listingsQuery.ToList();
return View(listings);
This works great, but I want to add a value in the list of results. Basically what I'm trying to do is something like this:
foreach (var item in listings)
{
this.Add("propertyType", "Home");
}
But obviously that doesn't work. I've tried doing ToArray() instead of ToList() and that got me nowhere.
Do you want to add a new property to each object in the List collection? If so, you can create a new type that inherits from whatever object type is in the list (hover over the listingsQuery variable - the type is inside the <> symbols) and add the new property to it:
public class MyNewType : ExistingTableType
{
public string PropertyType { get; set; }
}
Then inside of your query, project the properties into this new type:
var listingsQuery = (from s in db.tblListings
orderby s.listingID
select new NewType
{
listingID = s.listingID,
someOtherField = s.someOtherField
}
).Skip(100).Take(25);
Now you can cycle through each record and assign a value:
foreach(var record in listings)
{
record.ProperyType = "Home";
}
There are other ways of doing this as well (assuming you're using EF), for example, if you used raw SQL you can cast the result type directly into the new type (without having the map each field), but this should get you started.
If you want to add a new row to your listings collection you need to do listings.Add instead of this.Add
If you want to modify a value in your listing collection then you need to do
foreach(var item in listings)
{
item.PropertyName = value;
}