5
\$\begingroup\$

enter image description here

I'm designing a client (UI made with WPF) and server (WCF) that communicates with a device known as DDC (Direct Digital Control). A user on the client side can make requests to adjust the state of the DDC, then the server receives the information from the client and invokes appropriate operation on the DDC. DDC uses SOAP protocol for communication so I have a WSDL file that is being imported as service reference in Server, so invoking operations from the Server to DDC is very straight forward.

I'm having some trouble designing a solid data structure and interfaces for communication between UI and Engine, largely because there are a lot of operations defined in the WSDL file that I need to handle (like over 300 operations). Most of them return a string or a list of string, but some of them are defined as structures with a lot of fields.

For my interface, I initially thought about defining operationcontracts based on what parameters/return types some trivial function Require:

[ServiceContract]
public interface IDDCEngine
{
    [OperationContract(Name = "GetPointValueSpecific")]
    string GetPointValueSpecific(string host, string operation, out List<PointSpecificInformation> pointSpecificInfoList);

    [OperationContract(Name = "GetPointValueCommon")]
    string GetPointValueCommon(string host, string operation, out List<PointCommonInformation> pointCommonInfoList);

    [OperationContract(Name = "SetPointValueCommon")]
    string SetPointValueCommon(string host, string operation, List<PointCommonInformation> pointCommonInfo);

    [OperationContract(Name = "SetPointValueSpecific")]
    string SetPointValueSpecific(string host, string operation, List<PointSpecificInformation> pointSpecificInfo);

    [OperationContract(Name = "SendRequestListMultipleInMultipleOut")]
    string SendRequest(string host, string operation, List<string> parameters, out List<string> ddcResponse);

    [OperationContract(Name = "SendRequestSingleInMultipleOut")]
    string SendRequest(string host, string operation, string parameters, out List<string> ddcResponse);

    [OperationContract(Name = "SendRequestMultipleInSingleOut")]
    string SendRequest(string host, string operation, List<string> parameters, out string ddcResponse);

    [OperationContract(Name = "SendRequestSingleInSingleOut")]
    string SendRequest(string host, string operation, string parameter, out string ddcResponse);

    [OperationContract(Name = "SendRequestSingleOut")]
    string SendRequest(string host, string operation, out string ddcResponse);
}

The return type is string to indicate the status connection between UI/Engine (Validate/Error). Out parameter is the relevant response (data) received from DDC.

Here is an example of my engine implementation:

//Parameters: Single
//Response: Multiple
public string SendRequest(string host, string operation, string parameter, out List<string> value)
{
    value = new List<string>();
    string result = OPERATION_VALIDATE;
    try
    {
        DDCService.LGeDDCClient ddc = new DDCService.LGeDDCClient(DDC_NAMESPACE, host);
        switch (operation)
        {
            case "GetCPUMemoryStatus":
                int count;
                if (!int.TryParse(parameter, out count))
                    result = INVALID_PARAMETER;
                //Returns in the order of Process/Memory:
                string memory;
                string process = ddc.GetCPUMemoryStatus(count, out memory);
                value.Add(process);
                value.Add(memory);
                return OPERATION_VALIDATE;
            case "GetVersion":
                string kernel;
                string ramdisk = ddc.GetVersion(out kernel);
                value.Add(kernel);
                value.Add(ramdisk);
                return OPERATION_VALIDATE;
            default:
                result = UNDEFINED_OPERATION;
                return result;
        }
    }
    catch (Exception ex)
    {
        result = ex.Message;
        return result;
    }
    finally
    {
        DisplayTransmissionOnTextbox(operation, host, result);
    }
}

//Parameters: Multiple
//Response: Single
public string SendRequest(string host, string operation, List<string> parameter, out string value)
{
    value = String.Empty;
    string result = OPERATION_VALIDATE;
    try
    {
        DDCService.LGeDDCClient ddc = new DDCService.LGeDDCClient(DDC_NAMESPACE, host);
        switch (operation)
        {
            case "RequestUpgrade":
                if (parameter.Count != 2)
                    return INVALID_PARAMETER;
                int upgradeTarget;
                if (!int.TryParse(parameter[1], out upgradeTarget))
                    return INVALID_PARAMETER;
                value = ddc.RequestUpgrade(parameter[0], upgradeTarget);
                return OPERATION_VALIDATE;

            default:
                value = null;
                return UNDEFINED_OPERATION;
        }
    }
    catch (Exception ex)
    {
        result = ex.Message;
        return result;
    }
    finally
    {
        DisplayTransmissionOnTextbox(operation, host, result);
    }
}

//Parameters: Single
//Response: Single
public string SendRequest(string host, string operation, string parameter, out string value)
{
    value = String.Empty;
    string result = OPERATION_VALIDATE;
    try
    {
        DDCService.LGeDDCClient ddc = new DDCService.LGeDDCClient(DDC_NAMESPACE,host);
        switch (operation)
        {
            case "EndRestore":
                value = ddc.EndRestore(parameter);
                return OPERATION_VALIDATE;
            case "EndBackup":
                value = ddc.EndRestore(parameter);
                return OPERATION_VALIDATE;
            case "GetLogicSource":
                value = ddc.GetLogicSource(parameter);
                return OPERATION_VALIDATE;
            default:
                result = UNDEFINED_OPERATION;
                return result;
        }
    }
    catch (Exception ex)
    {
        result = ex.Message;
        return result;
    }
    finally
    {
        DisplayTransmissionOnTextbox(operation, host, result);
    }
}

//Parameters: None
//Response: Single
public string SendRequest(string host, string operation, out string value)
{
    value = String.Empty;
    string result = OPERATION_VALIDATE;
    try
    {
        DDCService.LGeDDCClient ddc = new DDCService.LGeDDCClient(DDC_NAMESPACE, host);
        switch (operation)
        {
            case "StartRestore":
                value = ddc.StartRestore();
                return result;
            case "StartBackup":
                value = ddc.StartBackup();
                return result;
            case "RequestReboot":
                value = ddc.RequestReboot();
                return result;
            case "StartUpgrade":
                value = ddc.StartUpgrade();
                return result;
            case "GetDatabaselist":
                value = ddc.GetDatabaselist();
                return result;
            case "GetProcesslist":
                value = ddc.GetProcesslist();
                return result;
            case "GetCurrenttime":
                value = ddc.GetCurrenttime();
                return result;
            case "GetUptime":
                value = ddc.GetUptime();
                return result;
            default:
                result = UNDEFINED_OPERATION;
                return result;
        }
    }
    catch (Exception ex)
    {
        result = ex.Message;
        return result;
    }
    finally
    {
        DisplayTransmissionOnTextbox(operation, host, result);
    }
}

Is this really a good approach of designing my interface for Client/Engine? My goal was to minimize the number of interfaces by stuffing many DDC operations, but I feel like what I'm writing is pretty dirty and can lead to problems. I would really appreciate any recommendations that can be made here.

\$\endgroup\$
  • \$\begingroup\$ Any final solution with full source code sample application ? IMHO, better samples for minimize learning curve are real applications with full source code and good patterns \$\endgroup\$ – Kiquenet Sep 25 '14 at 8:22
1
\$\begingroup\$

Rather than based on return types or parameters I think it would be best to base it on the functional grouping of the operations so that those more closely related are all grouped into one contract. Much like you might consider when naming namespaces and putting classes in there.

It's hard to know exactly from your example of what these groupings or whether even this is a good approach, however based on your existing contract I might start thinking can I seperate the point related functionality into a seperate IDDCPointService.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your response. I thought about grouping them by functional relevance myself but i was afraid of running into a problem where I have way too many operation contracts, like over 20 perhaps. Would you still recommend this approach despite the number of interfaces I may have to design? \$\endgroup\$ – l46kok Sep 16 '12 at 7:49
  • \$\begingroup\$ I guess only you can really answer that. My basic thought is still to look at the functional grouping. However there's a few good discussions on SO that might help you decide further e.g. stackoverflow.com/questions/1929728/…. A good start would be to create the groupings onto paper/design and then go from there before you even code anything. \$\endgroup\$ – dreza Sep 16 '12 at 9:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.