0

I am working on a web-service application with several (11) web-service calls.

For each web-service I need to populate the Soap Body from a string array like this:

if (aMessage[(int)DCSSCustomerUpdate_V3.Branch].ToString().Length != 0)
{
    wsSoapBody.Branch = aMessage[(int)DCSSCustomerUpdate_V3.Branch].ToString();
}

aMessage[int] is the string array, and [int] is defined by an enumerated constant - in this case it is defined like this:

private enum DCSSCustomerUpdate_V3
{
    MsgType = 0,
    MsgVersion = 1,
    WSName = 2,
    ReplyTo = 3,
    SourceSystem = 4,
    ...
}

The property names in the partial class are matched by the enumerated constant, so I guess I'd pass in the enumerated constant as well?

The partial class is defined in the wsdl like this:

public partial class DCSSCustomerUpdateType 
{
    private string instIdField;
    private string branchField;
    ...
}

Rather than doing this for each one separately (in each of 11 custom service classes), I wonder is there a way to pass in the partial class wsSoapBody (along with the string array) and loop through all the members of the class, assigning values from the string array?

EDIT:

I searched and found SO: 531384/how-to-loop-through-all-the-properties-of-a-class?

So I tried this:

    public static void DisplayAll(Object obj, string[] aMessage)
    {
        Type type = obj.GetType();
        PropertyInfo[] properties = type.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            string value = aMessage[property.Name].ToString();
            System.Diagnostics.Debug.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null));
        }
     }

but string value = aMessage[property.Name].ToString(); won't compile - as it is looking for an int returned from an enumerated constant...

so where do I go from there?

Community
  • 1
  • 1
Our Man in Bananas
  • 5,809
  • 21
  • 91
  • 148

3 Answers3

1

I don't know if I understood your question:

if (aMessage[(int)DCSSCustomerUpdate_V3.Branch].ToString().Length != 0)
{
    wsSoapBody.Branch = aMessage[(int)DCSSCustomerUpdate_V3.Branch].ToString();
}

So you have this enum DCSSCustomerUpdate_V3 which members match the property names of the wsSoapBody class, and you don't want to repeat code like the one above, but use a loop, correct?

You could simply loop over all elements of DCSSCustomerUpdate_V3 and set the value of the properties like:

// type of the enum; pass in as parameter
var enumType = typeof(DCSSCustomerUpdate_V3)

// get the type of wsSoapBody
var t = wsSoapBody.GetType();

// loop over all elements of DCSSCustomerUpdate_V3
foreach(var value in Enum.GetValues(enumType))
{
    if (aMessage[(int)value].ToString().Length != 0)
    {
        // set the value using SetValue
        t.GetProperty(value.ToString()).SetValue(wsSoapBody, aMessage[(int)value].ToString());
    }
}
sloth
  • 99,095
  • 21
  • 171
  • 219
  • Yes but if I want a generic method in my class common.cs, how would I define and pass in the enumerated constant? I have one enum for each of the different web-services...so `DCSSCustomerUpdate_V3` would in fact be a generic enum passed in ... – Our Man in Bananas Jan 27 '15 at 13:38
  • You could just pass in the type of the enum (let's call it `type_of_the_enum`) in question and instead of `foreach(DCSSCustomerUpdate_V3 value in Enum.GetValues(typeof(DCSSCustomerUpdate_V3)))` simply use `foreach(var value in Enum.GetValues(type_of_the_enum))` – sloth Jan 27 '15 at 13:41
1

Try so

DCSSCustomerUpdate_V3 t = (DCSSCustomerUpdate_V3)Enum.Parse(typeof(DCSSCustomerUpdate_V3), property.Name);
 string value = aMessage[(int)t].ToString();

you can use also the method Enum.TryParse

For generic Enum type more or less so

 public static void DisplayAll<TEnum>(Object obj, string[] aMessage) where TEnum : struct,  IComparable, IFormattable, IConvertible
        {
            if (!typeof(TEnum).IsEnum)
            {
                throw new ArgumentException("T must be an enumerated type");
            }

            Type type = obj.GetType();
            PropertyInfo[] properties = type.GetProperties();

            foreach (PropertyInfo property in properties)
            {
                TEnum t = (TEnum)Enum.Parse(typeof(TEnum), property.Name);
                string value = aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString();
                System.Diagnostics.Debug.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null));
            }
        }

see this post Create Generic method constraining T to an Enum

Community
  • 1
  • 1
Fabio
  • 1,890
  • 1
  • 15
  • 19
  • but what if `DCSSCustomerUpdate_V3` is generic, passed in, how would I pass it in? – Our Man in Bananas Jan 27 '15 at 13:40
  • so how do I actually set the values of my class - is it ` property.SetValue` before I return the class to my calling method? – Our Man in Bananas Jan 27 '15 at 14:04
  • Right, you must use is it property.SetValue with right type you can use TypeDescriptor.GetConverter(property.PropertyType ).ConvertFromInvariantString(value) for get – Fabio Jan 27 '15 at 14:10
  • I am trying to call the method like this `Common.DisplayAll(wsSoapBody, aMessage, DCSSCustomerUpdate_V3);` but I get these errors: **The type arguments for method 'EvryCardManagement.Common.DisplayAll(object, string[], System.Enum)' cannot be inferred from the usage. Try specifying the type arguments explicitly.** and **'EvryCardManagement.CustomerUpdate.DCSSCustomerUpdate_V3' is a 'type' but is used like a 'variable'** - where am I going wrong, and how can I return wsSoapBody populated? – Our Man in Bananas Jan 27 '15 at 14:55
  • 1
    Common.DisplayAll(....) (https://msdn.microsoft.com/en-us/library/twcad0zb.aspx) – Fabio Jan 27 '15 at 15:02
  • thanks for all your help on this, I have tried `Common.DisplayAll(aMessage, wsSoapBody);` but it still shows as *invalid arguments* – Our Man in Bananas Jan 27 '15 at 15:16
  • 1
    Common.DisplayAll(wsSoapBody, aMessage); where wsSoapBody is the type that you want fill I think – Fabio Jan 27 '15 at 15:17
  • yes but I'd like to pass in `wsSoapBody` **by Reference** so that it is returned populated? – Our Man in Bananas Jan 27 '15 at 15:20
0

so thanks to Fabio and Sloth, here is the final code we built:

public static void DisplayAll<TEnum>(Object obj, string[] aMessage) where TEnum : struct,  IComparable, IFormattable, IConvertible
/* 
 * see https://stackoverflow.com/questions/28168982/generically-populate-different-classes-members
 * 
 */
{
try
{
    // get the type of wsSoapBody
    Type type = obj.GetType();

    PropertyInfo[] properties = type.GetProperties();

    foreach (PropertyInfo property in properties)
    {
    try
    {
        if (Enum.IsDefined(typeof(TEnum), property.Name))
        {

        TEnum t = (TEnum)Enum.Parse(typeof(TEnum), property.Name, true);

        System.Diagnostics.Debug.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null) + "Type: " + property.PropertyType);

        // property.GetValue(obj, null).ToString() != "" &&
        if ( t.ToInt32(Thread.CurrentThread.CurrentCulture) < aMessage.GetUpperBound(0) && aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString() != "")
        {
            switch (property.PropertyType.ToString())
            {
            case "System.String":
                string value = aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString();
                property.SetValue(obj, value, null);
                break;
            case "System.Int32":
                int iValue = Convert.ToInt32(aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString());
                property.SetValue(obj, iValue, null);
                break;
            case "System.Int64":
                long lValue = Convert.ToInt64(aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString());
                property.SetValue(obj, lValue, null);
                break;
            case "System.DateTime":
                DateTime dtValue = DateTime.ParseExact(aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString(), "ddMMyyyy", System.Globalization.CultureInfo.InvariantCulture);
                property.SetValue(obj, dtValue, null);
                break;
            default:
                System.Diagnostics.Debugger.Break();
                break;
            }
        }
        else
        {
            logBuilder("Common.DisplayAll", "Info", "", property.Name + " is empty or outside range", "Index number: " + t.ToInt32(Thread.CurrentThread.CurrentCulture).ToString());

            System.Diagnostics.Debug.WriteLine(property.Name + " is empty or outside range", "Index number: " + t.ToInt32(Thread.CurrentThread.CurrentCulture).ToString());
        }
    }
    else
    {
        logBuilder("Common.DisplayAll", "Info", "", property.Name + " is not defined in Enum", "");
        System.Diagnostics.Debug.WriteLine(property.Name + " is not defined in Enum");
    }
    }
    catch (Exception ex)
    {
        logBuilder("Common.DisplayAll", "Error", "", ex.Message, "");
        emailer.exceptionEmail(ex);
        System.Diagnostics.Debugger.Break();
    }

    System.Diagnostics.Debug.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null));
    }
}
catch (Exception ex)
{
    logBuilder("Common.DisplayAll", "Error", "", ex.Message, "");
    emailer.exceptionEmail(ex);
    System.Diagnostics.Debugger.Break();
    //throw;
}
return;
}

and to call it, we use:

Common.DisplayAll<DCSSCustomerUpdate_V3>(wsSoapBody, aMessage);
Community
  • 1
  • 1
Our Man in Bananas
  • 5,809
  • 21
  • 91
  • 148