2

I created Enum ToFrendlyString function for my enums, but i cant use in Linq.

 public enum MyEnum
    {

        Queued = 0,           
        [Description("In progress")]
        In_progress = 2,            
        [Description("No answer")]
        No_answer = 6,

    }



  public static class EnumToFrendlyString
    {

        public static string ToFrendlyString(this Enum value)
        {
            return value.GetEnumDescription();
        }


        public static string GetEnumDescription(this Enum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());

            var attributes =
                (DescriptionAttribute[])fi.GetCustomAttributes(
                    typeof(DescriptionAttribute),
                    false);

            if (attributes.Length > 0)
                return attributes[0].Description;

            return value.ToString();
        }
    }

When i try to use this function in Linq, im getting error

  var res = collection.AsQueryable().Where(p => p.UserID == UserID).OrderByDescending(p=> p.DateCreated).Select(p => new MyClass
                {                          
                     Date = p.DateCreated.ToString(),
                     Status = p.Status.ToFrendlyString(),                        
                }).Take(10).ToList();

If i make another function in same class, like

 private string MyStatusToString(MyEnum status)
       {
           return status.ToFrendlyString();
       }

and change my Linq to use this function, then everything works.

Error

Expression of type 'DAL.MyEnum' cannot be used for parameter of type 'System.Enum' of method 'System.String ToFrendlyString(System.Enum)'
Steve Wilkes
  • 7,085
  • 3
  • 29
  • 32
Novkovski Stevo Bato
  • 1,013
  • 1
  • 23
  • 56

3 Answers3

2

I'm not sure you can use Enum as the Type for an extension method like that - try this instead. I've taken the liberty of tidying the code up a bit, feel free to ignore those changes :)

public static class EnumToFrendlyString
{
    public static string ToFrendlyString<T>(this T value)
        where T : struct
    {
        return value.GetEnumDescription();
    }

    public static string GetEnumDescription<T>(this T value)
        where T : struct
    {
        return EnumDescriptionCache<T>.Descriptions[value];
    }

    private static class EnumDescriptionCache<T>
        where T : struct
    {
        public static Dictionary<T, string> Descriptions =
            Enum.GetValues(typeof(T))
                .Cast<T>()
                .ToDictionary(
                    value => value,
                    value => value.GetEnumDescriptionForCache());
    }

    private static string GetEnumDescriptionForCache<T>(this T value)
        where T : struct
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException("Only use with enums", "value");
        }

        var descriptionAttribute = typeof(T)
            .GetField(value.ToString())
            .GetCustomAttributes(typeof(DescriptionAttribute), false)
            .Cast<DescriptionAttribute>()
            .FirstOrDefault();

        return (descriptionAttribute != null)
            ? descriptionAttribute.Description
            : value.ToString();
    }
}

I've added a private, generic class to cache the descriptions for your enum members to avoid lots of runtime use of reflection. It looks a bit odd popping in and out of the class to first cache then retrieve the values, but it should work fine :)

The warning I gave in this answer still applies - the enum value passed to the dictionary isn't validated, so you could crash it by calling ((MyEnum)5367372).ToFrendlyString().

Community
  • 1
  • 1
Steve Wilkes
  • 7,085
  • 3
  • 29
  • 32
  • You *can* use `Enum` in an extension method like that. – svick Feb 01 '13 at 18:21
  • Wow wow, looks like its working! If i don`t comment about any error then its 100% working. Can you give explanation why "where T : struct" do the magic here? About caching, since its generic function, do i must cache on first ToFrendlyString call since how i can init the cache dictonary? – Novkovski Stevo Bato Feb 01 '13 at 18:28
  • `where T : struct` just limits the extension method's applicability to value types - this method should work fine without it. As @svick points out you actually *[can](http://stackoverflow.com/questions/276585/enumeration-extension-methods)* use `Enum` as the type for an extension method, so I'll be honest and say I'm not 100% sure why it wasn't working when you did. I'll update the answer with a caching example :) – Steve Wilkes Feb 01 '13 at 18:38
0

I am not sure but it can be that you didn t add the DAL project yet to your current project(Add reference -> projects in solutins -> Dal). Then it might work. (I had a similar issue once and this was my solution)

Maximc
  • 1,722
  • 1
  • 23
  • 37
  • its already added, if it wasnt, Resharper or Build fill alert an error – Novkovski Stevo Bato Feb 01 '13 at 17:40
  • Not always. I had a constructor with a Enum but the project didn t had the project(with the neum) added in the reference, it only gave error on runtime with the same error. Adding the reference fixed it there for me. – Maximc Feb 01 '13 at 17:44
0

It seems the problem is that your collection is IQueryable<T> and the query provider is trying to translate your Select() into a query string.

One way to avoid that is to perform Select() in memory using IEnumerable<T>:

var res = collection.AsQueryable()
            .Where(p => p.UserID == UserID)
            .OrderByDescending(p=> p.DateCreated)
            .Take(10)
            .AsEnumerable()
            .Select(p => new MyClass
            {                          
                 Date = p.DateCreated.ToString(),
                 Status = p.Status.ToFrendlyString(),                        
            })
            .ToList();
svick
  • 236,525
  • 50
  • 385
  • 514