Home » Advertising » ASP.NET Core: Building an Enum Provider to Convert C# Enums to JavaScript

ASP.NET Core: Building an Enum Provider to Convert C# Enums to JavaScript

My previous post about ASP.NET Core and getting C# enums to JavaScript was primitive and used simple attributes on enums to detect ones we need in JavaScript. This blog post extends the idea and makes some generalizations to also support those enums that are located in libraries we don’t control or on what we don’t want to apply attributes.

JavaScriptEnum Attribute

We start again by defining the JavaScriptEnum attribute and some sample enums that are decorated with this attribute. The JavaScriptEnum attribute is a marker attribute and it doesn’t carry any functionality.

public class JavaScriptEnumAttribute : Attribute
{
}   

[JavaScriptEnum]
public enum PaymentTypeEnum
{
    CreditCard,
    Check,
    Cash
}   

[JavaScriptEnum]
public enum CustomerStatusEnum
{
    Regular,
    Gold,
    Platinum
}

Now let’s move away from web application specifics and suppose we have to work with an application that contains multiple libraries. We cannot force the JavaScriptEnum attribute into library projects, as we don’t want web specifics to pollute the so called lower layers. Also, we want to support system enums and enums from third-party libraries.

We need something more general than just a marker attribute. We need a provider and we start with defining an interface for this. This way we are able to support multiple implementations of the provider.

public interface IEnumProvider
{
    IEnumerableType GetEnumTypes();
}

The enum provider will be a class that gathers all enum types that are needed in JavaScript and returns them as an IEnumerableType. Notice that the code below gathers enums that are decorated with the JavaScriptEnum attribute and also adds two system enums to the return value.

public class EnumProvider : IEnumProvider
{
    public IEnumerableType GetEnumTypes()
    {
        var enums = new ListType();           

        enums.AddRange(GetJavaScriptEnums());
        enums.Add(typeof(CalendarWeekRule));
        enums.Add(typeof(ProcessorArchitecture));           

        return enums;
    }       

    private static IEnumerableType GetJavaScriptEnums()
    {
        return from a in GetReferencingAssemblies()
                from t in a.GetTypes()
                from r in t.GetTypeInfo().GetCustomAttributesJavaScriptEnumAttribute()
                where t.GetTypeInfo().BaseType == typeof(Enum)
                select t;
    }       

    private static IEnumerableAssembly GetReferencingAssemblies()
    {
        var assemblies = new ListAssembly();
        var dependencies = DependencyContext.Default.RuntimeLibraries;           

        foreach (var library in dependencies)
        {
            try
            {
                var assembly = Assembly.Load(new AssemblyName(library.Name));
                assemblies.Add(assembly);
            }
            catch (FileNotFoundException)
            { }
        }
        return assemblies;
    }        
}

NB! It’s possible to write more flexible and general enum providers. For example, enum providers can read information about enums to return from the application configuration. It is also possible to make it return all enums from given name spaces and so on. There are no limits to providers.

Before using an enum provider in web application views, we have to register it with built-in dependency injection so it will be available for views and view components.

services.AddSingletonIEnumProvider, EnumProvider();

Now, let’s write a view component that takes an enum provider and turns the returned enums into a JavaScript string. Here again, StringBuilder is used as a buffer.

public class EnumsToJavaScriptViewComponent : ViewComponent
{
    private readonly IEnumProvider _enumProvider;       

    public EnumsToJavaScriptViewComponent(IEnumProvider enumProvider)
    {
        _enumProvider = enumProvider;
    }       

    public TaskHtmlString InvokeAsync()
    {                      
        var buffer = new StringBuilder(10000);           

        foreach (var jsEnum in _enumProvider.GetEnumTypes())
        {
            buffer.Append("var ");
            buffer.Append(jsEnum.Name);
            buffer.Append(" = ");
            buffer.Append(EnumToString(jsEnum));
            buffer.Append("; rn");
        }           

        return Task.FromResult(new HtmlString(buffer.ToString()));
    }       

    private static string EnumToString(Type enumType)
    {
        var values = Enum.GetValues(enumType).Castint();
        var enumDictionary = values.ToDictionary(value = Enum.GetName(enumType, value));           return JsonConvert.SerializeObject(enumDictionary);
    }
}

Notice that the view component doesn’t need any views. It returns just HtmlString with JavaScript and that’s it. The building view for this would be pointless overkill.

The following code snippet shows us how to use the view component on the layout page.

script
@await Component.InvokeAsync("EnumsToJavaScript")
/script

And here is the result as seen in the browser.

script
var PaymentTypeEnum = { "CreditCard": 0, "Check": 1, "Cash": 2 };
var CustomerStatusEnum = { "Regular": 0, "Gold": 1, "Platinum": 2 };
var CalendarWeekRule = { "FirstDay": 0, "FirstFullWeek": 1, "FirstFourDayWeek": 2 };
var ProcessorArchitecture = { "None": 0, "MSIL": 1, "X86": 2, "IA64": 3, "Amd64": 4, "Arm": 5 };
/script

If some other casing is needed, then the enum provider can be changed. It also possible to create a JavaScript variable, formatter, and inject this to the enum provider.

Other Ways to Convert enums to JavaScript

It’s also possible to use other ways to get enums to JavaScript.

  • HtmlHelper extension method – it’s possible but it’s not very convenient to make it work with dependency injection. I don’t have any good ideas right now on how to do it in a way so that the code looks nice and there is no architectural pollution.
  • Extension method for Enum – we can replace this solution by a simple extension method but then we have to write one line of code per Enum in view where we want to define enums; I don’t like this solution much.
  • EnumProvider that returns JavaScript as a string – this solution has one good point: we don’t need a view component but the drawback is we need a base class where the method for creating a JavaScript string is held (we can use the provider easily in controller actions or when using view injection we can use it also directly in views).

From these methods, I would go with last one but there is one thing I don’t like – it binds an enum provider strictly to the web application and it has additional ballast when used in some other context. Of course, this is the matter of taste and like always – it depends!

Wrapping Up

I started this enum to JavaScript saga with converting one enum to JavaScript on classic ASP.NET. Step by step I fleshed out the solution described in this post as ASP.NET Core has good features like view injection and view components that make the framework more flexible and easier to use when architecture is more complex. Although the solution here may seem too big or too much like “corporate application architecture” it still keeps changes to enums logic in one place – if something changes then it is a matter of the provider and there is no need to change layout pages. Also this way is easier to test enum providers.

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

cover letter