2

I store this class

public class Customer
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string CustID { get; set; }
}

In this dictionary :

public static ConcurrentDictonary<string, Customer> Customers = new ConcurrentDictonary<string, Customer>();

The key is a unique string for each customer.

I am trying to find the cleanest thread-safe way to update properties of the customer's stored in the dictionary.

Sorry if the code above has any syntax issues, typed it in from a smartphone.

Here is what I’m currently doing:

Customer oCustomers = new Customer();
Customers.tryGetValue(ID, out oCustomers);

Customer nCustomer = new Customer();
nCustomer = oCustomer;
nCustomer.Firstname = NewValue;

Customers.tryUpdate(ID, nCustomer, oCustomer);

This works but seems so hacked to me, any suggestions would be great.

This was closed as a duplicated question that asks how to modify the ConcurrentDictionary in a thred-safe way. I'm asking how to modify individual customers, not the dictionary.

I have not found an answer on stack overflow and have searched for some time. Will someone please re-open this question so I can’t get the help I came here for.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 2
    `ConcurrentDictionary` doesn't provide a mechanism for thread-safe operations on contained entities. In your example I'd make `Customer` immutable and always update with a fresh copy (for atomicity of operation). – orhtej2 Oct 31 '17 at 08:19
  • 1
    Why did this get three upvotes? What is _"the cleanest thread safe way to update the values"_? What have you tried? – CodeCaster Oct 31 '17 at 08:26
  • Update how? For example you can replace one instance of `Customer` with another for a given key, that's one thing. But maybe you want to get `Cusomer` by key and update its properties (like `FirstName`) - that's another thing. – Evk Oct 31 '17 at 08:28
  • I will update my post above – William Hartley Oct 31 '17 at 08:32
  • @CodeCaster I have updated my question, could you please take a look at it. – William Hartley Oct 31 '17 at 09:02
  • @Evk I want to get customer by key and update its properties – William Hartley Oct 31 '17 at 09:14
  • @WilliamHartley I still fail to see how the duplicate doesn't answer your question. You've edited your post to say it's not the same; but you haven't explained *why* it's not the same. You're trying to add or update an item in the collection. That's exactly what the duplicate answers. – Rob Nov 03 '17 at 00:52
  • It answers how to update the key Value not the other properties in the object. – William Hartley Nov 03 '17 at 01:13
  • @WilliamHartley then edit the question to make it explicit that you are trying to update the *value's properties* in a thread-safe way. Creating a copy and replacing the original is *not* a bad idea anyway, that's how F# records work – Panagiotis Kanavos Nov 03 '17 at 09:27

3 Answers3

0

ConcurrentDictionary is a dictionary that is thread safe by default and all operations involving it are atomic by design.

Florin Toader
  • 337
  • 1
  • 9
0

ConcurrentDictionary is thread-safe. It just depends on what you expect from thread safety.

From MSDN

ConcurrentDictionary<TKey, TValue> is designed for multithreaded scenarios. You do not have to use locks in your code to add or remove items from the collection. However, it is always possible for one thread to retrieve a value, and another thread to immediately update the collection by giving the same key a new value.

Vadim Iarovikov
  • 1,941
  • 1
  • 14
  • 20
0

The ConcurrentDictionary is thread-safe. That doesn't mean that its contents are.

The easiest way to ensure thread-safety is to make the objects immutable and replace them with a new one when you want to change the values. That's how immutable types in functional languages like F# work.

Your code though, doesn't do that. It's still modifying the stored object. When you type nCustomer = oCustomer you don't clone the object, you change the variable to point to the original oCustomer.

You can make the class immutable by using only readonly properties which are initialized by the constructor :

public class Customer
{
    public string FirstName { get; }
    public string LastName { get; }
    public string CustID { get; }

    public Customer(string firstName,string lastName,string custID)
    {
        CustID = custID;
        FirstName=firstName;
        LastName=lastName;
    }
}

To update a customer, pull it from the dictionary, create a copy and call TryUpdate. Make sure to check for success. If TryUpdate fails it means some other thread modified the customer and you probably need to retry. Eg:

Customer old;
if (customers.TryGetValue(ID, out old))
{
    var newCustomer = new Customer(newName,old.LastName,old.CustID);
    if(!customers.TryUpdate(ID,newCustomer,old))
    {
        // Who moved my cheese ?
    }
}
else
{
    //No customer!
}

You'll have to decide what to do if the customer value has changed:

  • You can retry the update, thus overwriting any other updates.
  • You can try reloading the customer and make your update, assuming that whoever changed the customer changed one of the other fields.
  • You can stop trying and warn the user that the record has already changed
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236