简体   繁体   中英

Centralize ternary logic in EF6/LINQ

I seem to often (when dealing with enums) do things like this:

.Select(x => new {
    enumDesc = (
        x.status == GoodStatus ? "Good!"
        : x.status == BadStatus ? "Bad<angryface>"
        : x.status == Unknown ? "no clue"
    )
})

or

let enumDesc = (
    x.status == GoodStatus ? "Good!"
    : x.status == BadStatus ? "Bad<angryface>"
    : x.status == Unknown ? "no clue"
)

Some queries end up having dozens of these conditions, like a project I'm working on now. Doing it this way is nice because it can be translated into a SQL case statement so it's faster than iterating through after materialization. In my current project, however, I need to do this in more than one place/query. Is there any way to do this in a performant and reusable way (ie, translates to SQL) without refactoring the structures (eg moving the enum into a table)? I haven't been able to come up with one. If I could capture or alias just the ternary stuff, that would be good enough, or making an expression that "selects" from constants or attributes or something...

If you're open to avoiding if logic, consider the following:

There are often times where I see developers in C# lean towards using the DescriptionAttribute and combining that with an extension method to lookup the enum values' description value.

public enum Status
{
    [Description("no clue")]
    Unknown,
    [Description("Good!")]
    Good,
    [Description("Bad<angryface>")]
    Bad
}

public static class EnumExt
{
    public static string Description<T>(this T source)where T : struct, IConvertible
    {
        if (!typeof (T).IsEnum)
        {
            throw new ArgumentException("T must be an enumerated type");
        }

        var fi = source.GetType().GetField(source.ToString());
        var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof (DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : source.ToString();
    }
}

Here's a working .NET Fiddle I threw together quick to demonstrate this.

Otherwise, if you want to map a string to an enum value without the usage of attributes -- I'd suggest a switch instead...

You can use a method that implements an Expression<Func<T, T2>> , see example below:

    public enum Status
    {
        GoodStatus,
        BadStatus,
        Unknown
    }

    public class foo
    {
        public Status status;
    }

    public void Execute()
    {
        List<foo> list = new List<foo>
        {
            new foo
            {
                status = Status.BadStatus
            },
            new foo
            {
                status = Status.GoodStatus
            },
            new foo
            {
                status = Status.GoodStatus
            },
            new foo
            {
                status = Status.Unknown
            }
        };


        var descStatus = list.Select(GetStatus).ToList();
    }

    public Expression<Func<foo, object>> GetStatus(foo _foo)
    {
        return x => new
        {
            enumDesc = _foo.status == Status.GoodStatus
                ? "Good!"
                : _foo.status == Status.BadStatus
                    ? "Bad<angryface>"
                    : _foo.status == Status.Unknown ? "no clue" : ""
        };
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM