I have the following code which is called every 3 seconds continuously from a thread
public class SomeClass
{
List<Person> _list;
public SetList(List<Preson> list)
{
_list = list;
}
private void WorkToBeDoneEverythreeSeconds()
{
var filteredList= _list.Where(x= x.IsConditionValid());
//................Use the filtered list here.........
}
}
_list is a reference to a C# List owned by another class passed into this class. The list is updated from a different thread in its owner class. Sometimes updates happening while the Where clause is executed and Invalid operation is thrown.
What is the most efficient way to get a snapshot on the actual list when using the enumerator? I can think of creating another collection from the current collection, but doing this every 3 seconds might not be the best idea?
Related
I have a variable [User::WorkOrderProductIdList] in SSIS package containing records of a class object.
Work Order Product class
public class WorkOrderProduct
{
public Guid workOrderId;
public Guid workOrderProductId;
public static Guid WorkOrderId { get; set; }
public static Guid WorkOrderProductId { get; set; }
}
Main Script Task
public override void InputWOProduct_ProcessInputRow(InputWOProductBuffer Row)
{
ArrayList wopList = new ArrayList();
WorkOrderProduct wop = new WorkOrderProduct();
wop.workOrderId = pWorkOrderID;
wop.workOrderProductId = pWorkOrderProductID;
wopList.Add(wop);
}
Assign wopList to [User::WorkOrderProductIdList]
public override void PostExecute()
{
base.PostExecute();
Variables.WorkOrderProductIdList = this.wopList;
}
In another script task, it takes in [User::WorkOrderProductIdList] as ReadOnlyVariables.
May I know how can I loop through [User::WorkOrderProductIdList] and extract the values of workOrderId and workOrderProductId for each row?
I saw that my ArrayList [User::WorkOrderProductIdList] contains the records and values, but there are no functions when . on the field.
Population
Intellisense issue aside, you'll only ever have at most 1 row in there
You are using a Data Flow and within that, you have a Script Component acting as a Transformation.
In InputWOProduct_ProcessInputRow which fires for each row that passes through the component, you have
Every time a new row comes in, you are going to empty out the existing ArrayList and reinitialize it.
Instead, you need to have that variable at the class scope and have the initialization logic in the not-shown PreExecute method
ArrayList wopList;
// Or, if you wish to use the Generics
// List<WorkOrderProduct> wopList
public override void PreExecute()
{
base.PreExecute();
/*
* Add your code here
*/
this.wopList = new ArrayList();
// Or
// this.wopList = new List<WorkOrderProduct>();
}
Consumption
You use the ArrayList to hold the elements of your array but that is a weakly typed list.
We don't recommend that you use the ArrayList class for new development. Instead, we recommend that you use the generic List class.
When you're enumerating through it in your foreach loop, what is getting popped off the list is of type Object. Not only do I just "know" that, itellisense is telling you that all it knows is the type is Object because it's giving you the functions that everything has because they're all derived from Object.
Yes, the Watch window has inspection magic to show you what the values are but do you think the team that wrote the former is the same team that wrote the latter?
Since you "know" what the type should be, declare it as such.
foreach (WorkOrderProduct wopObj in ...
However, the next logical error, probably, is going to be in the accessing of
Variables.WorkOrderProductIdList itself. Your snipped image there shows you're shredding out the array in the PreExecute method. The sequence of operations is that the Data Flow is going to go through validation, then pre-execute sequences so at that point, it's going to shred the results of your array list and the value of wopObj_workOrderId is going to be the last element of your array.
I have a class with a static list as shown below:
public class Context
{
private static readonly List<Definition> definitions;
static Context()
{
definitions = LoadXML("path-to-xml-file.xml"));
}
public static List<Definition> GetDefinitions()
{
return definitions;
}
}
My problem is making calls to GetDefinitions() seems to return the list by reference instead of by value, because when I do this elsewhere in my code:
var defs = Context.GetDefinitions().ToList();
defs.ForEach(a =>
{
a.Name = a.Alias ?? a.Name;
});
all subsequent calls to Context.GetDefinitions() will return the modified list - not the original one, hence my conclusion that defs is not a value but a reference to the definitions list.
I tried adding the .ToList() in an attempt to decouple the reference but still I get the same result.
I am also open to a workaround, which allows me to use .Select() instead of .ForEach() in my sample code.
The problem is that the list does not store the items itself, but rather references to the items. Even if you create a new list (e.g. with ToList()), the referenced items stay the same.
In order to fix this, you need to clone the items in the list so that you have a independent copy of the data. You can implement ICloneable on the items and use return the list like this:
public static List<Definition> GetDefinitions()
{
return definitions.Select(x => (Definition)x.Clone()).ToList();
}
This way you create a new list that contains the cloned items. However, cloning an item is a tedious task if you need to clone a deeply nested class structure. For a flat structure, using MemberwiseClone is an easy way.
I noticed an issue with enumerating over a collection that was a result of a .Select<T>() I can enumerate over it, and modify each item in the collection. When I look at the collection after the enumeration is completed each item is left un-modified.
public class FooModel
{
public Guid? Id { get; set; }
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
IEnumerable<FooModel> foos = Enumerable.Range(0, 100)
.Select(m => new FooModel());
foreach(FooModel f in foos)
{
f.Id = Guid.NewGuid();
}
Assert.IsTrue(foos.All(foo => foo.Id.HasValue));
}
}
If I append .ToList() or .ToArray() to execute the enumerable, then it modifies the items within the collection.
I understand that the IEnumerable isn't executed after the .Select<T>, but when the foreach runs it creates an enumerator and executes the IEnumerable on the foo local field. Why isn't it referencing the same object that the Select<T> creates?
I've seen questions on when to use IEnumerable over List but I understand the difference between the two. Describing vs implementing more or less. My question is more in regards to why the foreach over a local field, executes the IEnumerable and doesn't operate on the same objects referenced in the local variable.
Why isn't it referencing the same object that the Select creates?
Because foo is using Enumerable.Range which enumerates on-the-fly. There is no underlying data store that you are iterating over.
When you call foreach you are executing the underlying query, which creates 100 FooModel objects on the fly, which you modify.
When you call .All, you execute the query again, which creates another 100 Foo objects on the fly that have not been modified.
When you hydrate the results via ToList or ToArray, you are then looping over a concrete collection, and your changes to the underlying objects will persist.
I have created a derived collection object to introduce some added functionality to filter the active records in the collection as shown in the below code snippet. How to achieve it as i want to just filter the same collection while keeping the original references in the filter without creating copy.
public class ExtendedTypes : List<ExtendedType>
{
public ExtendedTypes Active
{
get { return this.Where(x => x.IsActive).ToList(); } // Compile Error
}
}
Filtering an existing list
You mentioned that you wanted to just filter the existing list without keeping a copy. In this case, creating a List won't do, since creating a list from the subset will always create a new collection, not just a filter. List<T> is not a lazily-evaluated collection.
What you probably need to do is either define Active as IEnumerable<ExtendedType> and return the result of the Where directly (using LINQ's lazy implementation), or, if you're in WPF, use something like CollectionView as an additional filter on top of a collection, like this:
public ICollectionView ActiveTypes
{
get
{
if (_activeTypes == null)
{
_activeTypes = CollectionViewSource.GetDefaultView(myExtendedTypes);
_activeTypes.Filter = (type) => (type as ExtendedType).IsActive;
}
return _activeTypes;
}
}
You can now bind to ActiveTypes and get only a subset of the original list, filtered by the result of the Filter clause.
Creating a new List
However, assuming ExtendedType is a Reference type, you don't have to worry about copies of the items themselves being made by duplicating the list. If you don't mind creating a copy of the list with the same references, use my original answer:
The compiler is correct, in the sense that an ExtendedTypes is-a List<ExtendedType>, but not the other way around, and ToList() create a List<ExtendedType>.
There is, however, a simple workaround. Rather than ToList, just create a new ExtendedTypes with a constructor that initializes from a collection:
public class ExtendedTypes : List<ExtendedType>
{
public ExtendedTypes (IEnumerable<ExtendedType> items) : base(items)
{}
public ExtendedTypes Active
{
get { return new ExtendedTypes(this.Where(x => x.IsActive)); }
}
}
Let's say I have a list in a class which will be used in a multi threading scenario.
public class MyClass
{
List<MyItem> _list= new List<MyItem>();
protected object SyncRoot {
get {
return ((IList)_list).SyncRoot;
}
}
public void Execute1()
{
lock(SyncRoot)
{
foreach(var item in _list) DoSomething(item);
}
}
public void Execute2()
{
Item[] list;
lock(SyncRoot)
{
list=_list.ToArray();
}
for(var i=0;i<list.Length;i++) DoSomething(list[i]);
}
}
The method Execute1 is the 'normal' way to enumerate the list in a thread-safe way. But what about Execute2? Is this approach still thread-safe?
Access to the (copy of the) List is threadsafe in both scenarios. But of course the MyItem elements are not synchronized in any way.
The second form looks a little more expensive but it will allow Add/Remove on the original while the DoSomething()s are running. The array acts like a kind of snapshot, if that matches your requirements it could be useful. Note that you might as well use ToList().
It's safe as long as every other use of _list is also protected with the same lock statement. You are taking exclusive access to the list, copying its contents and then working on the copy (to which you also have exclusive access due to scoping). A bit wasteful at first sight, but a legitimate approach under certain circumstances.