17

I'm trying to manually build a WCF Data Service using a POCO data model and I cannot figure out how to properly expose enum values. Assuming a simple model like:

public class Order
{
   public int ID {get; set;}
   public string Description {get; set;}
   public OrderStatus Status {get; set;}
}

public enum OrderStatus
{
   New,
   InProcess,
   Complete
}

How do you expose the valuable information in the OrderStatus property via the OData WCF Data Service?

If you do nothing, the Data Service generates a runtime error (enum is invalid property). The only answer I've seen that at least resolves the error is to mark the enum property as Ignored, such as:

[System.Data.Services.IgnoreProperties("Status")]
public class Order ...

This works, but it forces you to "omit" valuable information from the service layer. Are there other options for working with enum values in WCF Data Services?

EDIT: Please notice this is WCF Data Services (aka Astoria). This is not raw WCF Services, in which case the answers are more clear.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Todd
  • 5,538
  • 1
  • 31
  • 34
  • It's recommended to avoid enums in webservices because they create subtle backwards compatible problems. See http://stackoverflow.com/a/788281/52277 – Michael Freidgeim Jul 21 '13 at 10:04

6 Answers6

16

Enums are currently not supported in WCF Data Services (the OData protocol doesn't support them either). The typical workaround is to use string and constant values, or integer and constant values.

Vitek Karas MSFT
  • 13,130
  • 1
  • 34
  • 30
  • I was afraid of that. Happy to have clear confirmation at least. I'm trying some "wrapper" workarounds so I don't have to make large changes to my model just to use Data Services. Hopefully one of the workarounds will work! – Todd Aug 26 '10 at 04:41
  • 1
    @Vitek: Enums are a tremendously common programming construct, and it's a bizarre step backwards to not use them. Are there any plans to include enum support in future versions of the OData standard, or will WCF Data Services provide optional support when both ends are known to be on the MSFT stack? – Eric J. Feb 06 '12 at 05:31
  • We understand that enums are common, unfortunately they never met tha bar so far. They are rather high on our list of things to do next though. – Vitek Karas MSFT Feb 06 '12 at 09:43
  • @Vitek: I posted a more general version of this question and would appreciate any insight you can offer http://stackoverflow.com/questions/3570249/using-enums-in-wcf-data-services BTW in spite of some rough edges, I'm a big fan of the work MSFT is doing in this area. – Eric J. Feb 08 '12 at 00:11
  • The link in your comment points back to this question, could you please repost the riht one? :-) – Vitek Karas MSFT Feb 08 '12 at 12:57
  • Oopps... http://stackoverflow.com/questions/9185116/coping-with-lagging-enum-support but looks like you found it already. – Eric J. Feb 08 '12 at 17:23
  • Please update your response, as enums are now supported by WCF. Each value must be decorated with the `[EnumMemberAttribute]` as well as the enum must be decorated with the '[DataContractAttribute]` – ohmusama Sep 27 '13 at 21:41
  • @ohmusama: He made it clear he was talking about WCF Data Services, not plain WCF services. They've [started work on this in August](https://data.uservoice.com/forums/72027-wcf-data-services-feature-suggestions/suggestions/1012609-support-enums-as-property-types-on-entities) though, so hopefully you'll be correct in a few months. – tne Dec 05 '13 at 13:08
5

Maybe we can "cheat" it with the below workaround:

[System.Data.Services.IgnoreProperties("Status")]
public class Order
{
   public int ID {get; set;}
   public string Description {get; set;}
   public OrderStatus Status {get; set;}
   public int StatusValue
   {
      get
      {
           return (int)this.Status;
      }
      set
      {
          // Add validation here
          this.Status = (OrderStatus)value;
      } 
   }
}

public enum OrderStatus
{
   New,
   InProcess,
   Complete
}
Boris Modylevsky
  • 3,029
  • 1
  • 26
  • 42
5

As follow-up, the "wrapper" approach is ultimately what worked. Essentially, a small class is written to wrap the enum and return primitive int values in the Data Service:

[IgnoreProperties("EnumValue")]
public class OrderStatusWrapper
{
    private OrderStatus _t;

    public int Value
    {
        get{ return (int)_t; }
        set { _t = (OrderStatus)value; }
    }

    public OrderStatus EnumValue
    {
        get { return _t; }
        set { _t = value; }
    }

    public static implicit operator OrderStatusWrapper(OrderStatus r)
    {
        return new OrderStatusWrapper { EnumValue = r };
    }

    public static implicit operator OrderStatus(OrderStatusWrapper rw)
    {
        if (rw == null)
            return OrderStatus.Unresolved;
        else
            return rw.EnumValue;
    }
}  

This was largely based on the advice given for working around EF4's enum limits:

http://blogs.msdn.com/b/alexj/archive/2009/06/05/tip-23-how-to-fake-enums-in-ef-4.aspx

Hope this technique helps others that follow.

qujck
  • 14,388
  • 4
  • 45
  • 74
Todd
  • 5,538
  • 1
  • 31
  • 34
2

Assuming DataContract Serialization, like so:

[DataContract]
public class Order
{
   [DataMember]
   public int ID {get; set;}
   [DataMember]
   public string Description {get; set;}
   [DataMember]
   public OrderStatus Status {get; set;}
}

[DataContract]
public enum OrderStatus
{
    [EnumMember]
    New,
    [EnumMember]
    InProcess,
    [EnumMember]   
    Complete
}
StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • This generally works for plain WCF Services, but in this case I'm asking about WCF Data Services (formerly known as Astoria). In my tests, this solution DOES NOT work for using enums in Data Services. Have you tested this with WCF Data Services? – Todd Aug 25 '10 at 21:34
  • Oops - apols. There is a dirty hack in the links on Troy's post which might work for you - add an extra serializable int to the entity and then cast the enum to the getter / setters (but ignore the enum) – StuartLC Aug 25 '10 at 21:43
0

You need to make the enum a Data Contract.

See here for an example: http://consultingblogs.emc.com/merrickchaffer/archive/2007/04/03/Passing-Enum-values-into-WCF-Service-operations.aspx

[Edit] Apparently, that's not always the case as seen here: Sharing Enum with WCF Service

Community
  • 1
  • 1
Troy Guinn
  • 27
  • 1
  • Thanks for the quick input, but I think you missed that this is WCF Data Services, not plain WCF. I've tried the typical DataContract/EnumMember attributes, but they do not seem to work with WCF Data Services (aka Astoria). – Todd Aug 25 '10 at 21:35
0

You need to write own QueryPrivider

    public object GetPropertyValue(object target, ResourceProperty resourceProperty)
    {
        object result = null;
        PropertyInfo info = target.GetType().GetProperty(resourceProperty.Name);
        if (info != null)
            result = info.GetValue(target, null);
        if (result is Enum)
            return Convert.ToInt32(result);
        return result;
    }


    public ResourceType GetResourceType(object target)
    {
        ResourceType result = null;
        Type tp = target.GetType();
        if (tp.IsEnum)
        {
            result =  ResourceType.GetPrimitiveResourceType(typeof(Int32));
            return result;
        }
        ....
        return result;
    }
Slava
  • 3,445
  • 1
  • 20
  • 16
  • Is this really for a wcf DATA service? If so, could you give more details on how to accomplish this? – Jerther Nov 26 '14 at 17:02