How to preserve list values after linq operation? - c#

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

Related

C# query basic BSON knowing only key and values using LINQ

I have MongoDB collection from which I would like to query documents knowing only names of the keys.
I cannot use hardcoded base class because amount on keys will change over time in runtime. But as user I will know the names of them.
I tried doing it like that:
var collection = database.GetCollection<BsonDocument>(mongoCollectionName);
List<BsonDocument> list = (from c in collection.AsQueryable<BsonDocument>()
where c.GetElement("serial").Value == 1
select c).ToList();
but with that i get {document}.GetElement("serial").Value is not supported
So is there a way to use linq to query basic BSONs ?
Thanks !
Just make the viewModel same as the properties in MongoDb.
and For getting serial column whose value is 1, just do like this,
public class ViewModel {
.... model Properties goes here
}
var SerialNo = 1;
var collection = mongoDatabase.GetCollection<ViewModel>("name of your collection");
var builder = Builders<ViewModel>.Filter;
builder.Eq(x => x.serial, SerialNo );
Is it necessary to use LINQ? I mean it is not possible for you to do something like this?
var collection = database.GetCollection<BsonDocument>(mongoCollectionName);
List<BsonDocument> list = collection.Find(Builders<BsonDocument>.Filter.Eq("serial", 1)).ToList();
or even convert it to Dictionary.

Calculations inside Foreach loop effecting the iterating list values

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.

C# use LINQ to query custom list and return new subset list

I have a list of a custom type called Holdings. I am trying to query the list based on one property of the Holdings object to return a new list of Holdings. The LINQ query below does work correctly but I would like to replace var unitHld with List unitHld but can't get the code to work.
var unitHld = from hld in _holdingList
where hld.FundCode == lookThroList[i].FundCode
select new Holding() { Currency = hld.Currency,
FundCode = lookThroList[i].FundCode,
IdSedol = hld.IdSedol,
Nominal = hld.Nominal * unitWgt,
Price = hld.Price };
This new list is then slightly altered before being added back to the original list (I know the logic sounds strange but please accept this is how it has to be done). However because unitHld is var the line below does not work.
_holdingList.Add(unitHld);
The following call only adds a single item (where the item must be the same type as the list's elements):
_holdingList.Add(unitHld);
But you want to add a range of items, so do it like this:
_holdingList.AddRange(unitHld);
where unitHld is IEnumerable<T> and T is the type of the list's elements.
(This answer assumes that holdingList is of type List<T>, and that T is in fact Holding for your example.)
See List.AddRange() for details.
C# is statically typed.
var is not a type, all it does is a shortcut for in your case typing IEnumerable<Holding>.
If you want the result to be List<Holding> then all you need to do is wrap your query in brackets and put .ToList() at the end.
However, to append this to another list, you don't need to do that. Simply call .AddRange on the other list.
Alternatively, you can use Concat
var bothLists = aList.Concat(anotherList);
I would like to replace var unitHld with List unitHld but can't get the code to work.
You need to call ToList() on the result of the query:
var unitHld = from hld in _holdingList
where hld.FundCode == lookThroList[i].FundCode
select new Holding() { Currency = hld.Currency,
FundCode = lookThroList[i].FundCode,
IdSedol = hld.IdSedol,
Nominal = hld.Nominal * unitWgt,
Price = hld.Price };
List<Holding> unitHldList = unitHld.ToList();
This new list is then slightly altered before being added back to the original list
Once the data is in unitHldList, you can alter it as needed.
the line below does not work. _holdingList.Add(unitHld);
When you add the content of a collection to a List<T>, use AddRange method instead of Add:
_holdingList.AddRange(unitHldList);
Try this:
_holdingList.AddRange(unitHld);

Linq to SQL select multiple columns

I just want to select 2 columns from a MSSQL DB using LINQ.
The SQL should be
select table.col1,table.col2 from table
I tried
IList<string> myResults =
(
from data in dbconn.table
where table.col5 == null
select new {
col1=data.Id.ToString(),
col2=data.col2
}
).Take(20).ToList();
but this didn't work.
It says
cannot convert type list <AnonymousType#1> to Ilist<string>
You are basically trying to fill a list of strings with the entries of a list of anonymous types, that won't work.
Have you tried something like this?:
var list = from data in dbconn.table
where table.col5 == null
select new {
col1=data.Id.ToString(),
col2=data.col2
}
Then you can easily use the entries in a loop for example
foreach(var element in list) {
//...
}
Or like a list
list.Take(20).ToList();
First of all, a list of strings (List<string>) can only have one single string in an element not two (what you are trying to do here) changing the type to var would fix your exception but not sure if that is the solution you want.
var myResults =
(
from data in dbconn.table
where table.col5 == null
select new {
col1=data.Id.ToString(),
col2=data.col2
}
).Take(20).ToList();
You can select multiple fields using linq Select as shown above in various examples this will return as an Anonymous Type. If you want to avoid this anonymous type here is the simple trick.
var items = myResults.Select(f => new [] { f.Col1, f.Col2 }).SelectMany(item => item).Distinct();
I think this solves the problem

Update object in IEnumerable<> not updating?

I have an IEnumerable of a POCO type containing around 80,000 rows
and a db table (L2E/EF4) containing a subset of rows where there was a "an error/a difference" (about 5000 rows, but often repeated to give about 150 distinct entries)
The following code gets the distinct VSACode's "in error" and then attempts to update the complete result set, updating JUST the rows that match...but it doesn't work!
var vsaCodes = (from g in db.GLDIFFLs
select g.VSACode)
.Distinct();
foreach (var code in vsaCodes)
{
var hasDifference = results.Where(r => r.VSACode == code);
foreach (var diff in hasDifference)
diff.Difference = true;
}
var i = results.Count(r => r.Difference == true);
After this code, i = 0
I've also tried:
foreach (var code in vsaCodes)
{
results.Where(r => r.VSACode == code).Select(r => { r.Difference = true; return r; }).ToList();
}
How can I update the "results" to set only the matching Difference property?
Assuming results is just a query (you haven't shown it), it will be evaluated every time you iterate over it. If that query creates new objects each time, you won't see the updates. If it returns references to the same objects, you would.
If you change results to be a materialized query result - e.g. by adding ToList() to the end - then iterating over results won't issue a new query, and you'll see your changes.
I had the same kind of error some time ago. The problem is that linq queries are often deferred and not executed when it appears you are calling them.
Quotation from "Pro LINQ Language Integrated Query in C# 2010":
"Notice that even though we called the query only once, the results of
the enumeration are different for each of the enumerations. This is
further evidence that the query is deferred. If it were not, the
results of both enumerations would be the same. This could be a
benefit or detriment. If you do not want this to happen, use one of
the conversion operators that do not return an IEnumerable so that
the query is not deferred, such as ToArray, ToList, ToDictionary, or
ToLookup, to create a different data structure with cached results
that will not change if the data source changes."
Here you have a good explanation with examples of it:
http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx
Regards
Parsing words pretty closely on #jonskeet's answer...
If your query is simply a filter and the underlying source objects are updated, the query will be reevaluated and may exclude these objects based on the filter condition in which case your query results will change on subsequent enumerations but the underlying objects will still have been updated.
The key is a lack of a projection to a new type as far as updating and persisting the changed objects.
ToList() is the usual solution to this issue and it will solve the problem if there is a projection to a new type but things gets cloudy in event your query filters but does not project. Updates to the query still affect the original source objects given everything is referencing the same object.
Again, parsing words but these edge cases can trip you up.
public class Widget
{
public string Name { get; set; }
}
var widgets1 = new[]
{
new Widget { Name = "Red", },
new Widget { Name = "Green", },
new Widget { Name = "Blue", },
new Widget { Name = "Black", },
};
// adding ToList() will result in 'static' query result but
// updates to the objects will still affect the source objects
var query1 = widgets1
.Where(i => i.Name.StartsWith("B"))
//.ToList()
;
foreach (var widget in query1)
{
widget.Name = "Yellow";
}
// produces no output unless you uncomment out the ToList() above
// query1 is reevaluated and filters out "yellow" which does not start with "B"
foreach (var name in query1)
Console.WriteLine(name.Name);
// produces Red, Green, Yellow, Yellow
// the underlying widgets were updated
foreach (var name in widgets1)
Console.WriteLine(name.Name);

Categories

Resources