The Stack Overflow podcast is back! Listen to an interview with our new CEO.
    Tweeted twitter.com/#!/StackProgrammer/status/595756106070228992
2 Added syntax highlighting. Prevented horizontal scrolling.
source | link
public enum PlatformType : short
{
    /// <summary>
    ///     Device is an Android.
    /// </summary>
    Android = 0,

    /// <summary>
    ///     Device is an Windows platform.
    /// </summary>
    Windows = 1,

    //// Can not place the name iOS.

    /// <summary>
    ///     Device is an iOS platform.
    /// </summary>
    Ios = 2,
}
public enum PlatformType : short
{
    /// <summary>
    /// Device is an Android.
    /// </summary>
    Android = 0,

    /// <summary>
    /// Device is an Windows platform.
    /// </summary>
    Windows = 1,

    //// Can not place the name iOS.

    /// <summary>
    /// Device is an iOS platform.
    /// </summary>
    Ios = 2,
}
public abstract class Operation
{
    protected Operation()
    {
        this.Logs = new List<OperationLog>();
    }

    public DateTime? CreationDate { get; set; }

    public virtual Device Device { get; set; }

    public int DeviceId { get; set; }

    public OperationStatus GeneralStatus { get; set; }

    public int Id { get; set; }

    public DateTime? LastUpdate { get; set; }

    public ICollection<OperationLog> Logs { get; internal set; }

    public DateTime? ReceiveDate { get; set; }
}
public abstract class Operation
{
    protected Operation()
    {
        this.Logs = new List<OperationLog>();
    }

    public DateTime? CreationDate { get; set; }

    public virtual Device Device { get; set; }

    public int DeviceId { get; set; }

    public OperationStatus GeneralStatus { get; set; }

    public int Id { get; set; }

    public DateTime? LastUpdate { get; set; }

    public ICollection<OperationLog> Logs { get; internal set; }

    public DateTime? ReceiveDate { get; set; }
}
public sealed class LockOperation : Operation
{
    /// <summary>
    ///     Gets or sets a value indicating whether the device must be locked or not.
    /// </summary>
    public bool Lock { get; set; }

    /// <summary>
    ///     Gets or sets the device's lock password.
    /// </summary>
    public string Password { get; set; }
}
public sealed class LockOperation : Operation
{
    /// <summary>
    /// Gets or sets a value indicating whether the device must be locked or not.
    /// </summary>
    public bool Lock { get; set; }

    /// <summary>
    /// Gets or sets the device's lock password.
    /// </summary>
    public string Password { get; set; }
}
public sealed class EnablementOperation : Operation
{
    /// <summary>
    ///     Gets or sets a value indicating whether the device should be enabled or disabled.
    /// </summary>
    public bool Enabled { get; set; }
}
public sealed class EnablementOperation : Operation
{
    /// <summary>
    /// Gets or sets a value indicating whether the device should be enabled or disabled.
    /// </summary>
    public bool Enabled { get; set; }
}
public abstract class Device<TOperation>
{
    public ICollection<TOperation> Operations { get; set; }
    ...
}

public sealed class AndroidDevice : Device<AndroidOperation> {...}

public abstract class Operation {...}

public abstract class AndroidOperation {...}

public sealed LockOperation : AndroidOperation {...}
public abstract class Device<TOperation>
{
    public ICollection<TOperation> Operations { get; set; }
    ...
}

public sealed class AndroidDevice : Device<AndroidOperation> {...}

public abstract class Operation {...}

public abstract class AndroidOperation {...}

public sealed LockOperation : AndroidOperation {...}
public enum PlatformType : short
{
    /// <summary>
    ///     Device is an Android.
    /// </summary>
    Android = 0,

    /// <summary>
    ///     Device is an Windows platform.
    /// </summary>
    Windows = 1,

    //// Can not place the name iOS.

    /// <summary>
    ///     Device is an iOS platform.
    /// </summary>
    Ios = 2,
}
public abstract class Operation
{
    protected Operation()
    {
        this.Logs = new List<OperationLog>();
    }

    public DateTime? CreationDate { get; set; }

    public virtual Device Device { get; set; }

    public int DeviceId { get; set; }

    public OperationStatus GeneralStatus { get; set; }

    public int Id { get; set; }

    public DateTime? LastUpdate { get; set; }

    public ICollection<OperationLog> Logs { get; internal set; }

    public DateTime? ReceiveDate { get; set; }
}
public sealed class LockOperation : Operation
{
    /// <summary>
    ///     Gets or sets a value indicating whether the device must be locked or not.
    /// </summary>
    public bool Lock { get; set; }

    /// <summary>
    ///     Gets or sets the device's lock password.
    /// </summary>
    public string Password { get; set; }
}
public sealed class EnablementOperation : Operation
{
    /// <summary>
    ///     Gets or sets a value indicating whether the device should be enabled or disabled.
    /// </summary>
    public bool Enabled { get; set; }
}
public abstract class Device<TOperation>
{
    public ICollection<TOperation> Operations { get; set; }
    ...
}

public sealed class AndroidDevice : Device<AndroidOperation> {...}

public abstract class Operation {...}

public abstract class AndroidOperation {...}

public sealed LockOperation : AndroidOperation {...}
public enum PlatformType : short
{
    /// <summary>
    /// Device is an Android.
    /// </summary>
    Android = 0,

    /// <summary>
    /// Device is an Windows platform.
    /// </summary>
    Windows = 1,

    //// Can not place the name iOS.

    /// <summary>
    /// Device is an iOS platform.
    /// </summary>
    Ios = 2,
}
public abstract class Operation
{
    protected Operation()
    {
        this.Logs = new List<OperationLog>();
    }

    public DateTime? CreationDate { get; set; }

    public virtual Device Device { get; set; }

    public int DeviceId { get; set; }

    public OperationStatus GeneralStatus { get; set; }

    public int Id { get; set; }

    public DateTime? LastUpdate { get; set; }

    public ICollection<OperationLog> Logs { get; internal set; }

    public DateTime? ReceiveDate { get; set; }
}
public sealed class LockOperation : Operation
{
    /// <summary>
    /// Gets or sets a value indicating whether the device must be locked or not.
    /// </summary>
    public bool Lock { get; set; }

    /// <summary>
    /// Gets or sets the device's lock password.
    /// </summary>
    public string Password { get; set; }
}
public sealed class EnablementOperation : Operation
{
    /// <summary>
    /// Gets or sets a value indicating whether the device should be enabled or disabled.
    /// </summary>
    public bool Enabled { get; set; }
}
public abstract class Device<TOperation>
{
    public ICollection<TOperation> Operations { get; set; }
    ...
}

public sealed class AndroidDevice : Device<AndroidOperation> {...}

public abstract class Operation {...}

public abstract class AndroidOperation {...}

public sealed LockOperation : AndroidOperation {...}
1
source | link

How do I model this relationship so that it is valid by construction?

Imagine a device class that represents a physical real world mobile device, with fields like Enabled, Platform, Model IMEI, etc. Then, an operation class, which is something that needs to be done to a certain device, like DeviceEnablementOperation or LockOperation. This is a 'one device to many operations' relationship, and an operation cannot exist without being related to an existing device. Can I model this in such a way that the system will not allow certain types of operations to devices with certain platforms? For example, an Android device would not allow a DeviceEnablementOperation attached to it, while a Windows Phone device would.

Our current modeling started out considering only Android devices. At the time, we created the concept of operations as a poor man's event source, so that we could track what was done against a certain device via our API. Later on, we started supporting iOS devices too, and we introduced the Platform field using an enumeration class to differentiate them, like this:

public enum PlatformType : short
{
    /// <summary>
    ///     Device is an Android.
    /// </summary>
    Android = 0,

    /// <summary>
    ///     Device is an Windows platform.
    /// </summary>
    Windows = 1,

    //// Can not place the name iOS.

    /// <summary>
    ///     Device is an iOS platform.
    /// </summary>
    Ios = 2,
}

At that point in time, the requirements were being met correctly, but now we need to restrict certain types of operations to certain platforms. The current model does not automatically restrict this, so the current implementation considers a hardcoded list of supported operations for each platform, and uses this to reject the creation of some operations to certain devices.

What I'd like to do is change the modelling so that this restriction is more robustly enforced. I thought about using inheritance and creating AndroidDevice, IosDevice and WindowsPhoneDevice classes, but I still fail to see how I could restrict the relationship with operations themselves to cover this requirement. A device has operations, but not all devices support all operations.

The modelling for the operations themselves is very straightforward inheritance, like this:

public abstract class Operation
{
    protected Operation()
    {
        this.Logs = new List<OperationLog>();
    }

    public DateTime? CreationDate { get; set; }

    public virtual Device Device { get; set; }

    public int DeviceId { get; set; }

    public OperationStatus GeneralStatus { get; set; }

    public int Id { get; set; }

    public DateTime? LastUpdate { get; set; }

    public ICollection<OperationLog> Logs { get; internal set; }

    public DateTime? ReceiveDate { get; set; }
}

And then derived operation types, like the ones I mentioned:

public sealed class LockOperation : Operation
{
    /// <summary>
    ///     Gets or sets a value indicating whether the device must be locked or not.
    /// </summary>
    public bool Lock { get; set; }

    /// <summary>
    ///     Gets or sets the device's lock password.
    /// </summary>
    public string Password { get; set; }
}

and

public sealed class EnablementOperation : Operation
{
    /// <summary>
    ///     Gets or sets a value indicating whether the device should be enabled or disabled.
    /// </summary>
    public bool Enabled { get; set; }
}

Is it possible to model this to better reflect the new requirement that I want? I'd somehow need a way to tell if a given operation is supported for a given device, and only allow the relationship to exist if the operation was supported.

I suppose I could try something using generics and inheritance on both the device and operation classes, for example:

public abstract class Device<TOperation>
{
    public ICollection<TOperation> Operations { get; set; }
    ...
}

public sealed class AndroidDevice : Device<AndroidOperation> {...}

public abstract class Operation {...}

public abstract class AndroidOperation {...}

public sealed LockOperation : AndroidOperation {...}

But this particular approach would need multiple inheritance to work, and since I'm using C# it is not possible. I could then try using interfaces to differentiate operations, but them I suspect it would be a problem when mapping this to a database later using an ORM.

I also can't really break everything into their own completely isolated classes, because I need the general concept of "devices that have operations", regardless of platform or operation type. That's why the base classes exist in our current model.