3

I am building a Soap Body for a web-service, and there are dozens of optional fields.

Currently I have been handling these like this:

wsSoapBody.OrderType = aMessage[(int)cardCreate.OrderType].ToString();
wsSoapBody.ActivateFlag = Convert.ToInt32(aMessage[(int)cardCreate.ActivateFlag].ToString()); //P-02925;

if (aMessage[(int)cardCreate.ShipDate].ToString() != ""){
                wsSoapBody.ShipmentDate = Convert.ToDateTime(aMessage[(int)cardCreate.ShipDate].ToString()); //P-02925;
        }

wsSoapBody.ShipmentMethodCard = aMessage[(int)cardCreate.ShipMethodCard].ToString();
wsSoapBody.ShipmentMethodPin = aMessage[(int)cardCreate.ShipMethodPIN].ToString();

The CardCreate you see in those value assignments is an enumerated constant in the class cardCreate defined as below:

namespace EvryCardManagement
{
    class CardCreate
    {
        #region Variables

        private DCSSCardCreateType req;
        private DCSSCardCreateResponseType rsp;
        private DCSSCardCreate_V3_0Service stub;

        public string tokenID { get; set; }

        private enum cardCreate
        {
            MsgType = 0,
            MsgVersion = 1,
            WSName = 2,
            ReplyTo = 3,
            SourceSystem = 4,
            Timestamp = 5,
            UniqueMessageID = 6,
            SFDCContext = 7,
            InstitutionID = 8,
            CardNumber = 9,
            Version = 10,
            ProductID = 11,
            AccountNumber = 12,
            CustomerID = 13,
            CustomerNumber = 14,
            EmbossName1 = 15,
            Expiry = 16,
            FeeMonth = 17,
            ChargeAccountNo = 18,
            PINMethod = 19,
            CardFlag = 20,
            AddressTypeCard = 21,
            AddressTypePIN = 22,
            OrderType = 23,
            ActivateFlag = 24,
            ShipDate = 25,
            ShipMethodCard = 26,
            ShipMethodPIN = 27,
            FirstName = 28,
            LastName = 29,
            CardAddress1 = 30,
            CardAddress2 = 31,
            CardAddress3 = 32,
            CardAddress4 = 33,
            CardAddress5 = 34,
            CardAddress6 = 35,
            CardPostCode = 36,
            CardCity = 37,
            CardCountry = 38,
            PINName = 39,
            PINAddress1 = 40,
            PINAddress2 = 41,
            PINAddress3 = 42,
            PINAddress4 = 43,
            PINAddress5 = 44,
            PINAddress6 = 45,
            PINPostCode = 46,
            PINCity = 47,
            PINCountry = 48,
            Validfrom = 49,
            Note = 50,
            MakeCheckStatus = 51,
            EmbossName2 = 52,
            PAmount = 53,
            PAmountLength = 54,
            GKIndicator = 55,
            CreditLimit = 56,
            CardDesignNo = 57,
            ExtPictureID = 58,
            BulkID = 59,
            AccountNo2 = 60
        }

so, rather than doing them all one by one as I have been doing, is it possible to loop through the wsSoapBody (which is defined in the web-service) and for each one, get the corresponding value from the aMessage (which is defined as an array like this string[] aMessage)

EDIT

I have the below code to loop through, but I want to assign to the wsSoapBody and I am stuck:

foreach (cardCreate cItem in (cardCreate[])Enum.GetValues(typeof(cardCreate)))
 {
 }

(the above correction was suggested as an edit by Steve Lillis that was rejected due to a conflict)

so I don't know how then to assign the values to each element for example I want to set

wsSoapBody[cItem].value = aMessage[(int)CardCreate[cItem]` 

or I also tried:

wsSoapBody[cItem] = aMessage[(int)cItem].ToString();

but am having trouble making it work (or even compile) due to lack of knowledge

EDIT #2:

I have also looked at GetNames as possibly I want the names and tried:

        foreach (string name in Enum.GetNames(typeof(cardCreate)))

        {
            wsSoapBody[name] = aMessage[(int)name].ToString();
        }

But I cannot apply indexing with [] to an expression of type 'DCSSCardCreateType'

thanks

Our Man in Bananas
  • 5,687
  • 20
  • 88
  • 144
  • 4
    Have you looked at `Enum.GetValues`? – Jon Skeet Jan 08 '15 at 16:42
  • possible duplicate of [Can You Loop Through All Enum Values?](http://stackoverflow.com/questions/972307/can-you-loop-through-all-enum-values) – CompuChip Jan 08 '15 at 16:45
  • Hi Jon, yes, I have used `foreach (cardCreate cItem in (cardCreate[])Enum.GetValues(typeof(cardCreate))) { }` **BUT** I don't know how then to assign the values to each element for example I want to set `wsSoapBody[cItem].value = aMessage[(int)CardCreate[cItem]` but am having strouble making it work – Our Man in Bananas Jan 08 '15 at 16:46
  • I am editing the question, I see that it isn't clear that I can get some of the way, but am having trouble assigning the values... – Our Man in Bananas Jan 08 '15 at 16:48
  • @CompuChip: I have edited the question and title to clarify that I have the enumerated loop but don't know how to assign values from my string array – Our Man in Bananas Jan 08 '15 at 16:52
  • It is unclear to me at what stage this is happening, is this done in the client after getting a soap message, or in the server while processing a soap message? – ΩmegaMan Jan 08 '15 at 17:16
  • I asked because one can decorate the enum with attributes (which would be my answer to resolve this issue) if server side is processing this action. But the soap message to the client will strip off those decorations. – ΩmegaMan Jan 08 '15 at 17:24
  • @OmegaMan: I am creating the Soap Message in our local system, that will call the web service which is running in Norway. – Our Man in Bananas Jan 08 '15 at 17:25

1 Answers1

3

Why not place the values onto the enum itself and then enumerate?

For example using System.ComponentModel Description attribute we can add that information to the enum itself such as:

public enum cardCreate
{
  [Description("General Response")]
  MsgType = 0,

  [Description("V2.0")]
  WSName = 2,

  [Description("OmegaMan")]
  ReplyTo = 3,

  [Description("Windows 10")]
  SourceSystem = 4,
}

So when we call a special method to enumerate the enum where we can extract that text and use it appropriately later such as:

myextensions.GetEnumValues<cardCreate>()
            .Select (ceEnum => new
                        {
                            Original   = ceEnum,
                            IndexValue = (int)ceEnum,
                            Text       = ceEnum.GetAttributeDescription()
                        })

The dynamic entity will look like this after the projection (the select):

enter image description here

Sweet! Now we have all the information in a easy consumable entity which provides all the information needed.

What? You need more than a string description? Then create a custom attribute on the enum and have all items/types of data to return as needed. For that see my blog article C# Using Extended Attribute Information on Objects.


Here are the extension methods used in the above example:

public static class myextensions
{
   public static IEnumerable<T> GetEnumValues<T>()
   {
       Type type = typeof( T );

       if (!type.IsEnum)
           throw new Exception( string.Format("{0} is not an enum.", type.FullName ));

       FieldInfo[] fields =
           type.GetFields( BindingFlags.Public | BindingFlags.Static );


       foreach (var item in fields)
           yield return (T)item.GetValue( null );
   }


  /// <summary>If an attribute on an enumeration exists, this will return that
   /// information</summary>
   /// <param name="value">The object which has the attribute.</param>
   /// <returns>The description string of the attribute or string.empty</returns>
   public static string GetAttributeDescription( this object value )
   {
       string retVal = string.Empty;
       try
       {
           retVal = value.GetType()
                         .GetField( value.ToString() )
                         .GetCustomAttributes( typeof( DescriptionAttribute ), false )
                         .OfType<DescriptionAttribute>()
                         .First()
                         .Description;

       }
       catch (NullReferenceException)
       {
           //Occurs when we attempt to get description of an enum value that does not exist
       }
       finally
       {
           if (string.IsNullOrEmpty( retVal ))
               retVal = "Unknown";
       }

       return retVal;
   }

}
ΩmegaMan
  • 26,526
  • 10
  • 91
  • 107
  • I don't know if this is the exact answer that I want, but you put the work in, and it will probably do what I need, so thanks! – Our Man in Bananas Jan 13 '15 at 21:11
  • So if I implement this how could I loop through my `wsSoapBody` class setting all the values from my `aMessage[]` array of string values? – Our Man in Bananas Jan 15 '15 at 21:31
  • @OurManInBananas you would need to reflect off the instance and beforehand provide a mapping to the enum which you then could gather the values. – ΩmegaMan Jan 15 '15 at 21:33
  • yes that was what I was really asking in my question, or should I ask a new question for that? We have 12 web-services similar to `cardCreate` and they all have this problem where I need to set dozens of optional and mandatory fields in the `wsSoapBody`, so was looking for a way to re-use the same method for each one of them... – Our Man in Bananas Jan 15 '15 at 21:36
  • @OurManInBananas I would ask a different question, for it will give you the freedom to illicit responses geared to the final answer. – ΩmegaMan Jan 15 '15 at 21:55
  • Thanks, I'll ask a question about that. – Our Man in Bananas Jan 15 '15 at 21:56
  • I have asked the question, hoping it is clear enough [SO: Generically populate different classes members](http://stackoverflow.com/questions/28168982/generically-populate-different-classes-members) – Our Man in Bananas Jan 27 '15 at 11:46