14

I have seen how to convert a ConcurrentDictionary to a Dictionary, but I have a dictionary and would like to convert to a ConcurrentDictionary. How do I do that?... better yet, can i set the link statement to be a ConcurrentDictionary?

var customers = _customerRepo.Query().Select().ToDictionary(x => x.id, x => x);
Community
  • 1
  • 1
MrM
  • 21,709
  • 30
  • 113
  • 139

4 Answers4

30

Use ConcurrentDictionary<TKey, TValue> Constructor (IEnumerable<KeyValuePair<TKey, TValue>>) constructor which can accept a dictionary object like:

Dictionary<int, string> dictionary = new Dictionary<int, string>();
dictionary.Add(1,"A");
dictionary.Add(2, "B");

ConcurrentDictionary<int,string> concurrentDictionary = 
             new ConcurrentDictionary<int, string>(dictionary);

can i set the LINQ statement to be a ConcurrentDictionary?

No. You can not.. There is no extension method available to create ConcurrentDictionary in LINQ. You can either create your own extension method or you can use the ConcurrentDictionary constructor in your LINQ query while projecting results.

Habib
  • 219,104
  • 29
  • 407
  • 436
  • Well you could project the results to a dictionary using LINQ, but you would have to write your own `ToConcurrentDictionary` extension method to do it. (You might be able to get rid of the intermediate dictionary and insert directly using a `foreach` and taking in a key and value selector delegate). – Scott Chamberlain Nov 21 '14 at 14:51
  • You *can* use LINQ, with the constructor you mentioned. Create a KeyValuePair from the original enumerable with the desired key and values, then pass it to the constructor, eg. `myEnumerable= ... Select(item=>new KeyValuePair(item.Id,item); ... new ConcurrentDictionary(myEnumerable);`. No need for a ForEach, although an extension method would make the code slightly cleaner – Panagiotis Kanavos Nov 21 '14 at 14:52
7

Why not write your own extension method:

  public static class ConcurrentDictionaryExtensions {
    public static ConcurrentDictionary<TKey, TElement> ToConcurrentDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) {
        if (source == null) throw new ArgumentNullException("source");
        if (keySelector == null) throw new ArgumentNullException("keySelector");
        if (elementSelector == null) throw new ArgumentNullException("elementSelector");

        ConcurrentDictionary<TKey, TElement> d = new ConcurrentDictionary<TKey, TElement>(comparer ?? EqualityComparer<TKey>.Default);
        foreach (TSource element in source)
            d.TryAdd(keySelector(element), elementSelector(element));

        return d;
    }

    public static ConcurrentDictionary<TKey, TSource> ToConcurrentDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
        return ToConcurrentDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance, null);
    }

    public static ConcurrentDictionary<TKey, TSource> ToConcurrentDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer) {
        return ToConcurrentDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance, comparer);
    }

    public static ConcurrentDictionary<TKey, TElement> ToConcurrentDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector) {
        return ToConcurrentDictionary<TSource, TKey, TElement>(source, keySelector, elementSelector, null);
    }

    internal class IdentityFunction<TElement> {
        public static Func<TElement, TElement> Instance
        {
            get { return x => x; }
        }
    }

}

Simply adopted the code from the .Net framework.

Oliver
  • 43,366
  • 8
  • 94
  • 151
  • Not sure why but unless you pass a comparer this will throw. See [source](https://referencesource.microsoft.com/#mscorlib/system/Collections/Concurrent/ConcurrentDictionary.cs,332) – MotKohn Sep 21 '20 at 19:23
  • @MotKohn: You're right. Fixed the code according to the [source](https://referencesource.microsoft.com/#mscorlib/system/Collections/Concurrent/ConcurrentDictionary.cs,177) – Oliver Sep 22 '20 at 09:12
4

A LINQ-To-Objects statement is ultimately an IEnumerable so you can pass it to the ConcurrentDictionary constructor, eg:

var customers = myCustomers.Select(x => new KeyValuePair(x.id, x));
var dictionary=new ConcurrentDictionary(customers);

This may not work with other providers. Linq to Entities for example, converts the entire LINQ statement to SQL and can't projection to a KeyValuePair. In this case you may have to call AsEnumerable() or any other method that forces the IQueryable to execute, eg:

var customers = _customerRepo.Customers.Where(...)
                             .AsEnumerable()
                             .Select(x => new KeyValuePair(x.id, x));
var dictionary=new ConcurrentDictionary(customers);

Select() with no arguments is not an IEnumerable or IQueryable method so I suppose it's a method provided by some other ORM. If Select() returns an IEnumerable you can use the first option, otherwise you can use AsEnumerable()

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Note that you can do `AsEnumerable()` [on a enumerable](http://msdn.microsoft.com/en-us/library/vstudio/bb335435(v=vs.100).aspx) and it will not hurt anything. – Scott Chamberlain Nov 21 '14 at 15:23
0

Or just a have method:

 private ConcurrentDictionary<TKey, TValue> ToConcurrent<TKey, TValue>(Dictionary<TKey, TValue> dic) {
        return new ConcurrentDictionary<TKey, TValue>(dic);
    }

and then do:

var customers = _customerRepo.Query().Select().ToDictionary(x => x.id, x => x);
var concurrentDic = ToConcurrent(customers);

Personally, I'm going with the extension method that I just updated...

Denis
  • 11,796
  • 16
  • 88
  • 150