-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Defining aliases for enum values #20202
Comments
@xamadev Can you explain a bit more what you mean by "aliases" in this case? |
Sure. Imagine you have a status column in database which contains string values of "S" or "E". In code I'd like to map them to an enum with values Enum.Success and Enum.Error which I call aliases in my question. Therefore I need a mapping between the String value and the corresponding enum value. |
@xamadev The way to handle that is with a custom value converter. Something like: public enum Worm
{
Slow,
Earth
}
public class WormConverter : ValueConverter<Worm, string>
{
public WormConverter()
: base(v => Convert(v), v => Convert(v))
{
}
private static string Convert(Worm value)
{
return value switch
{
Worm.Slow => "S",
Worm.Earth => "E",
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
};
}
private static Worm Convert(string value)
{
return value switch
{
"S" => Worm.Slow,
"E" => Worm.Earth,
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
};
}
} |
All right, thanks for your help. Maybe it's worth introducing a data annotation to define such aliases / mappings? What do you think? |
@xamadev I'm not sure if it should be a new attribute - but this feels like it should line up with the enum as string serialization attributes like System.Runtime.Serialization.EnumMember |
@CZEMacLeod EnumMember is interesting--I wasn't aware of it before now. We will discuss. |
@ajcvickers The class I'm not sure about Microsoft's
Although it does look like there are extension points for it with custom converters. It does seem like there is a fair bit of re-inventing the wheel going on here between all these projects doing value conversion and specifically |
From my point of view, adding the possibility to automatically export the enum text and value mapping into the field description would be a great feature to increase the readability of such columns... db-tools like dbeaver show the column description if you hover the column. |
I wrote a value converter that can handle /// <summary>
/// A value converter for enums that use the <see cref="EnumMemberAttribute"/> value as the stored value.
/// </summary>
/// <remarks>
/// It turns out that EF Core doesn't respect the EnumMemberAttribute yet.
/// </remarks>
/// <typeparam name="TEnum">The enum type.</typeparam>
public class EnumMemberValueConverter<TEnum> : ValueConverter<TEnum, string> where TEnum : struct, Enum
{
// Yeah, these dictionaries are never collected, but they're going to generally be small and
// there's not going to be too many of them.
static readonly Dictionary<string, TEnum> StringToEnumLookup = CreateStringToEnumLookup();
static readonly Dictionary<TEnum, string> EnumToStringLookup = CreateEnumToStringLookup(StringToEnumLookup);
public EnumMemberValueConverter()
: base(
value => GetStringFromEnum(value),
storedValue => GetEnumFromString(storedValue))
{
}
static TEnum GetEnumFromString(string value) => StringToEnumLookup.TryGetValue(value, out var enumValue)
? enumValue
: default;
static string GetStringFromEnum(TEnum value) => EnumToStringLookup.TryGetValue(value, out var enumValue)
? enumValue
: string.Empty;
static Dictionary<string, TEnum> CreateStringToEnumLookup() =>
Enum.GetValues<TEnum>().ToDictionary(value => value.GetEnumMemberValueName(), value => value);
static Dictionary<TEnum, string> CreateEnumToStringLookup(Dictionary<string, TEnum> stringToEnumLookup) =>
stringToEnumLookup.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
}
public static class EnumExtensions {
public static string GetEnumMemberValueName(this Enum? item)
=> item.GetEnumAttributeValue<EnumMemberAttribute>(attr => attr.Value);
static string GetEnumAttributeValue<TAttribute>(this Enum? item, Func<TAttribute, string?> getAttributeValue)
where TAttribute : Attribute
{
var field = item?.GetType().GetField(item.ToString());
if (field is null)
return string.Empty;
var memberAttribute = field.GetCustomAttribute<TAttribute>(inherit: false);
if (memberAttribute is not null)
{
return getAttributeValue(memberAttribute) ?? field.Name;
}
return field.Name;
}
} Note that these dictionaries are never collected until the process goes down, but the expectation is there won't be too many values so the overhead should be small. If an enum value doesn't have the attribute, then the normal conversion to/from Just register the converter in your modelBuilder.Entity<MyEntity>()
.Property(a => a.EnumProperty)
.HasConversion(new EnumMemberValueConverter<EnumPropertyType>()); That will let you use enum aliases until EF supports it natively. |
@haacked This looks like a good start, although I think it would be better to use a source generator to build the converter with the finite list of values rather than reflection to build the dictionaries. |
Hi,
is it possible to define aliases for enum's values like using the Description annotation?
Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
1.xml](https://github.com/aspnet/EntityFramework.ApiDocs/blob/live/dotnet/xml/Microsoft.EntityFrameworkCore.Storage.ValueConversion/EnumToStringConverter
1.xml)The text was updated successfully, but these errors were encountered: