views:

60

answers:

3

Hi,

I am writing a simple generic update extension for IEnumerable, this method used to join given 2 List of business objects or dictionaries using the given keys and updating the specific field.

public static void Update<TOuter, TInner, TKey>(this IEnumerable<TOuter> outer, IEnumerable<TInner> Inner, Func<TOuter, TKey> OuterKeySelector, Func<TInner, TKey> InnerKeySelector,Action<TOuter,TInner> updator)
        {
            ILookup<TKey, TInner> innerLookup = Inner.ToLookup(InnerKeySelector, element => element);

            foreach (TOuter outerItem in outer)
            {
                TKey key = OuterKeySelector(outerItem);
                if (innerLookup.Contains(key))
                {
                    foreach (TInner innerItem in innerLookup[key])
                    {
                        updator(outerItem, innerItem);
                    }
                }
            }

        }

This works fine in normal objects, for ex:

      List<testObject> obj1 = new List<testObject>()
       {
           new testObject(){fruitId=1,name="mango"},
           new testObject(){fruitId=2,name="grapes"},
           new testObject(){fruitId=2,name="grapes"},
           new testObject(){fruitId=4,name="kivi"},
       };

       List<testObject> obj2 = new List<testObject>()
       {
           new testObject(){fruitId=2,name="apple"},
           new testObject(){fruitId=4,name="orange"},
       };

        obj1.Update(obj2,
             tx => tx.fruitId,
             ty => ty.fruitId,
            (tx,ty)=>tx.name=ty.name);

But, i cannot use this method with dictionary,

       Dictionary<string, int> first = new Dictionary<string, int>()
       {
           {"a",1},
           {"b",2},
           {"c",9},
           {"e",5},               
       };
       Dictionary<string, int> second = new Dictionary<string, int>()
        {
           {"a",8},
           {"b",2},
           {"e",20}               
       };
       var kk = 0;

       first.Update(second,
           f1 => f1.Key,
           s1 => s1.Key,
           (f1, s1) => f1.Value = s1.Value);

it gives the below error

Property or indexer 'System.Collections.Generic.KeyValuePair.Value' cannot be assigned to -- it is read only

I know there is a restriction, by MSDN

Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection.

is there a hack/workaround to achive the same functionality in a generic manner?

Cheers

+2  A: 

Your code seems reasonable if you look it in the same manner for List as for Dictionary.

In your list example you have a List<MyMutableType> and you update a Property of the MyMutableType object in a certain position in the list.

In your dictionary example you have a Dictionary<Key,MyNotMutableType> and you are trying to substitute a MyNotMutableType instance in a certain position with another MyNotMutableType instance, you're not trying to simply change a Property of the same object instance.

Following the approach used for List you should have a dictionary like: Dictionary<Key,MyMutableType> and in your updater delegate you should only update a Property of the MyMutableType.

Hope this help (and sorry for my poor english)

digEmAll
thanks digEmAll, i understand your point... :)
Ramesh Vel
A: 

The error you are getting is not because of the inability to modify the dictionary while you are enumerating a collection, this is a runtime error. It is, as the error says, that the KeyValuePair does not have a setter on the Value parameter. so f1.Value == s1.Value is not allowed. Essentially KeyValuePair is immutable as you can't change the values.

If you want this type of functionality I would recommend that you create a more specific Update that takes a Dictionary specifically rather than a IEnumerable.

In terms of working around the fact that dictionaries are readonly when the are being enumerated I don't know an answer.

btlog
Also lists cannot be modified during enumeration...
digEmAll
+1  A: 

Another way to solve this problem is to switch your inner & outer classes. Class that needs to be updated should be inner, so this avoids the modiable collection to enter into the enumuration..

       second.Update(first1,
           s1 => s1.Key,
           f1 => f1.Key,   
          (f1, s1) => first1[s1.Key] = f1.Value);

Thanks andyp & digEmAll

Cheers

Ramesh Vel