5

Can LinqToDB store the value of an Enum property as the string value of the Enum instead of the integer value?

public enum OrderType
{
    New,
    Cancel
}

[Table("Orders")]
public class Order
{
    [PrimaryKey, Identity]
    public int OrderId { get; set; }

    [Column]
    public OrderType Type { get; set; }
}

How can I get LinqToDB to store "New" or "Cancel" to the database instead of 0 or 1?

Update: Seems the answer for LinqToDB is to set the MapValue attribute on the enum. I had been trying to find where to specify it on the data object.

public enum OrderType
{
    [LinqToDB.Mapping.MapValue(Value = "NEW")]
    New,
    [LinqToDB.Mapping.MapValue(Value = "CANCEL")]
    Cancel
}

This will store the specified value on the enum as text in the database.

BiaachMonkie
  • 141
  • 1
  • 7
  • Why would you want to store a value that is naturally a numeric value as a string value? – Erik Philips Apr 14 '15 at 05:05
  • I would presume he/she wants to be able to query the data from the database and have meaningful results. It might be best to have a table(s) in the database that represents the enum(s) that you have within code. Then you can join on these to get meaningful data for queries and also performance elsewhere not using them. This will create a problem of having two points to update when changing the enum values though. – bobwah Apr 14 '15 at 08:12
  • The reason for needing the string value of the enum is because I'm trying to use this with an existing database, considering options to migrate away from Castle Activerecord that is currently in use. – BiaachMonkie Apr 14 '15 at 17:08
  • What did you end up doing? – DWright Apr 15 '15 at 14:07
  • 1
    @DWright I added an update to the question, the solution is add a MapValue attribue on the enums. – BiaachMonkie Apr 15 '15 at 18:07

3 Answers3

9

Seems the answer for LinqToDB is to set the MapValue attribute on the enum. I had been trying to find where to specify it on the data object.

public enum OrderType
{
    [LinqToDB.Mapping.MapValue(Value = "NEW")]
    New,
    [LinqToDB.Mapping.MapValue(Value = "CANCEL")]
    Cancel
}
BiaachMonkie
  • 141
  • 1
  • 7
  • 2
    See also the author's article [Using MapValueAttribute to control mapping with linq2db](http://blog.linq2db.com/2015/05/using-mapvalueattribute-to-control.html). – CodeFox May 10 '16 at 14:59
5

You may have already considered using a wrapper type to facilitate conversion to and from the enum, but just in case, here goes.

A wrapper allows implicit conversions you cannot defined on the enum

You could implement a wrapper that allows some very helpful implicit conversions (which you cannot do on the enum type itself). The you could have the Column type be your wrapper type, yet in the DB it could be stored as a string because it implicitly converts to a string type, and retrieved as a string type, yet be returned as your wrapper type, again because of implicit conversions.

It can be used wherever the enum is used

Further, because it has an implicit conversion, your wrapper type can stand in wherever an the enum type is used in code.

Demo

Here's a demonstration of such a wrapper type and the cool conversions it enables pretty seamlessly:

using System;

namespace ConsoleApplication
{
    public static class Program
    {
        public enum OrderType
        {
            typeA,
            typeB
        }

        public class OrderTypeWrapper
        {
            private OrderType enumValue;

            public static implicit operator OrderType(OrderTypeWrapper wrapper)
            {
                return wrapper.EnumValue;
            }

            public static implicit operator OrderTypeWrapper(OrderType ot)
            {
                var wrapper = new OrderTypeWrapper();
                wrapper.EnumValue = ot;
                return wrapper;
            }

            public static implicit operator OrderTypeWrapper(String orderTypeValue)
            {
                var wrapper = new OrderTypeWrapper();
                wrapper.StringValue = orderTypeValue;
                return wrapper;
            }

            public static implicit operator String(OrderTypeWrapper wrapper)
            {
                return wrapper.StringValue;
            }

            public static implicit operator OrderTypeWrapper(int intValue)
            {
                var wrapper = new OrderTypeWrapper();
                wrapper.IntValue = intValue;
                return wrapper;
            }

            public static implicit operator int(OrderTypeWrapper wrapper)
            {
                return wrapper.IntValue;
            }

            public int IntValue
            {
                get
                {
                    return (int)enumValue;
                }
                set
                {
                    enumValue = (OrderType)value;
                }
            }
            public String StringValue
            {
                get
                {
                    return Enum.GetName(typeof(OrderType), enumValue);
                }
                set
                {
                    try
                    {
                        //Use TyeParse to do something other than throw exception in presence of bad string value.
                        //Perhaps set it to an "invalid" signifying value of the enum, instead.
                        enumValue = (OrderType)Enum.Parse(typeof(OrderType), value, true); //throws exception on bad value
                    }
                    catch (ArgumentException ae)
                    {
                        var message = String.Format("Attempt to make a bad string value of \"{0}\" into an OrderType. ", value);
                        throw new Exception(message, ae);
                    }
                }
            }
            public OrderType EnumValue
            {
                get
                {
                    return enumValue;
                }
                set
                {
                    enumValue = value; ;
                }
            }
        }

        public class Order
        {
            public OrderType TypeOfOrder { get; set;}
            public String StringValueOfAnOrderType { get; set; }

            public override String ToString()
            {
                return String.Format("TypeOfOrder={0}; StringValueOfAnOrderType={1}",TypeOfOrder,StringValueOfAnOrderType);
            }
        }

        public static void Main(string[] args)
        {
            Order order = new Order();
            Order order2 = new Order();
            Order order3 = new Order();

            //straight through, not that useful but shows that wrapper can stand in
            order.TypeOfOrder = (OrderTypeWrapper)OrderType.typeB;   
            //int to string name of the enum value                
            order.StringValueOfAnOrderType = (OrderTypeWrapper)1; 

            Console.WriteLine("order: " + order);

            //string to enum value, shows that case insensitive works, see third parameter of Parse.Enum to control this.
            order2.TypeOfOrder = (OrderTypeWrapper)"TYPEB";
            //enum value to string name of the enum value
            order2.StringValueOfAnOrderType = (OrderTypeWrapper)OrderType.typeB;  

            Console.WriteLine("order2: " + order2);

            //Not that helpful as you could also cast via (OrderType), but is shows that ints work, too.
            order3.TypeOfOrder = (OrderTypeWrapper)1;                   

            //Will helpfully blow up if the string type is wrong.
            try
            {
                order3.StringValueOfAnOrderType = (OrderTypeWrapper)"typeC";  
            }
            catch(Exception ex)
            {
                Console.WriteLine("Exception encountered: " + ex.Message);
            }

            Console.WriteLine("order3: " + order3);

            var key = Console.ReadKey();
        }
    }
}
DWright
  • 9,258
  • 4
  • 36
  • 53
0

Why you need to get those value into string? You must consider your database performance and size to do those thing. If you want the simple way to do that you can use something like this:

public class Order
{
    [PrimaryKey, Identity]
    public int OrderId { get; set; }

    [Column]
    public OrderType Type { get; set; }

    private string _DisplayType;
    [NotMapped]
    public string DisplayType
    {
        get
        {
            if (Type != null)
                _DisplayType = Type.ToString();
            else
                _DisplayType = string.Empty;

            return _DisplayType;
        }
        set
        {
            if (_DisplayType != value)
            {
                _DisplayType = value;
            }
        }

    }
}

If you want to display it somewhere else, you can bind the DisplayType. If you want to change the value on database, you can use the Type property.