2

I have the following code snippet

[DataMember]
public StateEnum DeviceState
    {
        get
        {
            return _deviceState;
        }
        set
        {               

            if (IsArmed)
            {
                _deviceState = value;
            }
            else
            {
                _whileDisarmState = value;
            }              
        }
    }

while IsArmed is Boolean,
and StateEnum has the following struct

    [DataContract]
public enum StateEnum
{
    [EnumMember]
    ERROR,
    [EnumMember]
    CONNECTED,
    [EnumMember]
    DISCONNECTED,
    [EnumMember]
    DISARMED,
    [EnumMember]
    ALARM,
    [EnumMember]
    WARNING,

}

I have a method which invokes the server and get list of objects, which DeviceState is one of its members. When I break on server side just before returning the list, one of the objects have a DeviceState = StateEnum.DISCONNECTED value,
but when I break on the client side, I have for the same object the StateEunm.ERROR value.

I'm pretty sure the problem is with the IsArmed Boolean. Trying to DataMember it didn't help, as well as adding [KnownType(typeof(StateEnum))] to the object itself.

One more thing, this enum is known on client side, it worked perfectly fine before adding this if statement.

Update

I'll try to explain my logic with more code snippets,
though this is complex code, not sure I can describe it fully.

Here are the relevant properties and private members:

[DataMember]
    public bool IsArmed
    {
        get { return _isArmed; }
        set { _isArmed = value; }
    }

public StateEnum WhileDisarmState
    {
        get { return _whileDisarmState; }
        set { _whileDisarmState = value; }
    }

#region Private Members

    private StateEnum _deviceState;
    private bool _isArmed;
    private StateEnum _whileDisarmState;

    #endregion

DeviceState & WhileDisarmState get their initial value in the constructor:

IsArmed = true;    
WhileDisarmState = StateEnum.DISCONNECTED;
DeviceState = StateEnum.DISCONNECTED;

DeviceState holds crucial part on client UI,
It is displayed as "Unreachable" when it is disarmed. It can still be updated from different areas of code, by different methods - but I don't want to display it, just save it somewhere else - and display the recent state when it is armed again.
This is the reason for my "not symmetric" setter.
Here is the implementation for the Arm & Disarm (invoked from client side)

public virtual void Arm()
    {
        IsArmed = true;
        DeviceState = WhileDisarmState;
        IsUpdated = true;
    }

    public virtual void DisArm()
    {
        DeviceState = StateEnum.DISARMED;
        IsArmed = false;
        IsUpdated = true;
    }

I hope I gave you as much info as you need.
Thanks,
Naor.

nafarkash
  • 359
  • 6
  • 24
  • Where is the bool defined? Is it overridden maybe while the list is completed and sent? I suspect you need to show more code (a minimal, complete, verifiable example) –  Aug 28 '16 at 06:35
  • Thanks for your partial update! Anyway this "it can still be updated from different areas of code, by different methods" is likely the cause of the issue :) just double check if IsArmed and WhileDisarmState on the client are what you'd expect or try to see what happens on the server until just before the object is returned... –  Aug 28 '16 at 08:12

2 Answers2

1

You see this situation because your getter and setter are not symmetric.

It appears that the value of DISCONNECTED is being set on the _whileDisarmState variable, leaving _deviceState variable in its initial state of default(StateEnum), which happens to be ERROR.

To fix this problem change getters and setters of your data transfer object to be symmetric. Ideally, remove all business logic from them, such as paying attention to the IsArmed state.

Instead of asking should I update this member or the other, let the setter determine it for me.

The logic does not belong on a DTO, though. Currently, the state of the object depends on the order in which the properties are set. This may be OK for a business object, when you control the order in which you set the properties. However, this is absolutely not acceptable for data transfer object, when WCF implementation decides in what order the properties are to be set.

For example, consider passing an object that is armed and connected. If WCF sets IsArmed first, and then DeviceState, you end up with an armed object in connected state (i.e. everything is fine). If, however, WCF sets DeviceState first, you end up with an armed object in error state, with _whileDisarmState set to connected. In other words, you end up with an object in different state on the other side of a transfer, which is precisely the problem that you are trying to solve.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • I updated my question. You are correct with your answer, but I did this in order to simplify my code when invoking the setter DeviceState. Instead of asking should I update this member or the other, let the setter determine it for me. – nafarkash Aug 28 '16 at 07:30
1

A first, very important point is that you have to separate the implementation from the contract.

Keep the standard setters and getters for your DeviceState and implement a separate SetDeviceState method based on the current code of your setter

and change all the references accordingly from DeviceState = to SetDeviceState

After that, in case of need, if you have the possibility to build and test a "debug" version of your service I'd suggest to add a temporary TraceHelper property to the object like a simple string that is updated in append mode each time DeviceState and IsArmed are updated

and possibly tracking also all the caller methods...

so that you can more easily spot if there has been a bug in the update sequence logic..