views:

700

answers:

6

I have a flag attribute enumeration that is behind a web service as follows:

[Serializable,Flags]
public enum AccessLevels
{
    None = 0,
    Read = 1,
    Write = 2,
    Full = Read | Write
}

My problem is the consumer of my web service does not have the original constant values of the enum. The resulting proxy class client side has something that amounts to this:

{
   None = 1,
   Read = 2,
   Write = 4,
   Full = 8
}

And thus when the consumer is checking for "Read" access this will be false even when "testItem" is "Full"

((testItem & Svc.Read) == Svc.Read)

How can I properly provide flags over a web service?

EDIT:

According to this article it may not be possible to do what I am looking to do. Ivan Krivyakov states

Imperfect Transparency of Enums

It turns out that enums are not as transparent as we'd like them to be. There are three sticky issues:

  1. If server-side code declares an enum and assigns specific numeric values to its members, these values will not be visible to the client.
  2. If server-side code declares a [Flags] enum with "compound" mask values (as in White = Red|Green|Blue), it is not properly reflected on the client side.
  3. If server or client transmits an "illegal" value which is outside of the scope of the enum, it causes an exception in XML de-serializer on the other side.

So I wonder if this is just a limitation and is not possible.

A: 

I had a similar problem and got round it by adding another web service to return the currect flag values first.

Those then became the values I used in the compare.

Might not be the cleanest solution but it works.

Edit:

My original answer suggested that the values are passed across as a separate web service rather than within an enum.

However, poking around, it appears that an enumeration of 0,1,2 being mapped to 1,2,4 across a web service (even that the [Flags] attribute is set) is a common problem.

The solution suggested by a number of people is to modify the original enumeration definition and start from 1 rather than 0.

nzpcmad
Can you elaborate a little?
Brawndo
A: 

Flags should be multiples of two, and in your case your flags are (0,1,2,3). Try change your definition of the struct to:

[Serializable,Flags]
public enum AccessLevels{    
None = 1,    
Read = 2,    
Write = 4,    
Full = Read | Write}

And see if it works better.

(I hope Im not making a fool of myself, its late and Im on my way to the bed.. )

Stefan
That still doesn't work. I've seen this problem before. With the numbering you mentioned your consumer will still see the following:public enum AccessLevels { None = 1, Read = 2, Write = 4, Full = 8,}
Rorzilla
@Rorzilla yes that is indeed the case still. @Stefan I can "fix" it by removing the "Full" and "None" then the proxy class will become Read = 1 and Write = 2 but I would still like to provide the user with the none option so they don't have to "just know" something and dont have to use a value of 0
Brawndo
@Stefan - No, flags should be individual bits, of which 1 is the first. None should *always* be defined as 0 because that means there are no bits set. If None = 1 then what is 0? ReallyNone?
Greg Beech
@Greg, sounds fair. I have seen 0 being used as "Default" in some cases though.
Stefan
A: 

One option would be to provide a more verbose class instead of the enum e.g.

[Serializable]
public class AccessPermission{
 public boolean None{get;set;}
 public boolean Read{get;set;}
 public boolean Write{get;set;}
 public boolean Full{get;set;}

 public AccessPermission(AccessLevels level){
  None = false;
  Read = false;
  Write = false;
  Full = false;

  switch(level){
  case AccessLevels.None:
   break;
  case AccessLevels.Read:
   Read = true;
   break;
  case AccessLevels.Write:
   Write = true;
   break;
  case AccessLevels.Full:
   Read = true;
   Write = true;
   Full = true;
   break;
  }
 }
}

The other option I can see is providing a method in what ever language they are using to successfully interoperate the integer you are sending. The flag operator means that c# does bit masking to find if a individual flag is marked

0001 -> None
0010 -> Read
0100 -> Write
0110 -> Full

so to check for any permission you should see if that bit is set


public static boolean CanRead(int accessLevel){
 return (accessLevel | 2) > 0 // return true if read bit set, using bitwise or
}

public static boolean CanWrite(int accessLevel){
 return (accessLevel | 4) > 0 // return true of write bit set.
}

Please note this second solution is more fragile, if you change the definition of accessLevels you client will silently miss behave.

David Waters
+1  A: 

I have done extensive research on this and found that it is not possible to serialize enumeration constants through a web service. Note that to accomplish your goal you don't need the enumerations None or Full. These two enumerations can be implied with read / write combination:

You could assume full access if your AccessLevels = Read | Write and none if your AccessLevels = 0 [nothing]

Your enumerations would look like this:

[Serializable,Flags]
public enum AccessLevels
{
    Read = 1,
    Write = 2
}
Rorzilla
I am thinking that this is the route the I may need to take here. I would really like to be able to have the "None" option spelled out for my users however.
Brawndo
A: 

Hi Chris, that only works if using WCF (Windows Communication Foundation) not the older WSDL. Thanks though.