I am writing an application that uses ASP.NET MVC
for its front end, and for the back end, it uses RavenDB
to store data. This has so far worked out great, but I am hitting a huge wall with the use of enum
in selecting things.
The problem comes with front end interaction. I have many places where I want enums to be selectable via drop down lists and the like. This works okay on the surface, but I ran across an issue with the actual relationship between enums and JSON as a language/format. These issues were not overcome by using the traditional [JsonConverter(typeof(StringEnumConverter))]
attribute in my models.
Essentially put, MSDN reports the following; from the MSDN
Enumeration member values are treated as numbers in JSON, which is different from how they are treated in data contracts, where they are included as member names. For more information about the data contract treatment, see Enumeration Types in Data Contracts.
For example, if you have public enum Color {red, green, blue, yellow, pink}, serializing yellow produces the number 3 and not the string "yellow".
All enum members are serializable. The EnumMemberAttribute and the NonSerializedAttribute attributes are ignored if used.
It is possible to deserialize a nonexistent enum value - for example, the value 87 can be deserialized into the previous Color enum even though there is no corresponding color name defined.
A flags enum is not special and is treated the same as any other enum.
Now what this meant is that even if I used fancy footwork to convert my enums into strings for my dropdownlists, they still get saved back to the database as integers; Or rather it is more sufficient to say that they get saved in Raven as strings, but still 'considered' as integers upon deserialization back to the javascript components/MVC.
Using a lot of time and patience, I did manage to find "work arounds" for this; They were cumbersome and obtuse, I did not like them. It made plugging my enums into various UI javascript packages difficult. In the end, I elected to just do away with enums entirely and go with a different approach.
So what I did is create a base class Listable
.
public class Listable : IHasIdentity, IHasName, IHasLabel, IHasStyle { public Listable(): base() { } public string Id { get; set; } public string Name { get; set; } public string Label { get; set; } public string CSS { get; set; } public string Description { get; set; } public int Order { get; set; } public string Keyword { get; set; } }
Listable is used to form a collective association within my program, so there is another base class known as a Listing
.
[RavenTag("listings")] public class Listing : IHasIdentity, IHasName, IMayTransform, IRavenIdentity { public Listing(): base() { Items = new List<Listable>(); } public string Id { get; set; } public string Name { get; set; } public List<Listable> Items { get; set; } /// <summary> /// The identity for raven to generate for this document when it is created. /// </summary> /// <returns> /// The complete identity for Raven to use for this object. /// </returns> public string Identity() { return string.Format("listings/{0}", Name.AsIdentity()); } }
So then, a Listing
has many items, and upon being created it uses an extension method to save an identity for each item in the listing, defined here;
public static class TypeExtensions { public static Models.Listing ToIdentities(this Models.Listing obj) { // set the identity of each item in the collection foreach (var item in obj.Items) item.Id = string.Format("list/{0}/{1}", obj.Name.ToLower().Replace(" ", "-").Replace("/", "-"), item.Name.ToLower().Replace(" ", "-").Replace("/", "-")); return obj; } }
This seems like a lot of work just to get a simple list, but in the end it paid off because now I have a user interface where my staff members can define any number of lists, with any number of items, and edit them; And then these can be easily bound to javascript controls in any way we need like standard objects. An example of such a list is as follows;
{ "Name": "Genders", "Items": [ { "Id": "list/genders/male", "Name": "Male", "Label": "Male", "CSS": null, "Description": null, "Order": 0, "Keyword": null }, { "Id": "list/genders/female", "Name": "Female", "Label": "Female", "CSS": null, "Description": null, "Order": 1, "Keyword": null }, { "Id": "list/genders/none", "Name": "None", "Label": "", "CSS": null, "Description": null, "Order": 2, "Keyword": null } ] }
And then these are easy to query, a simple service on my controllers makes it trivial.
[HttpGet] [Route("list/named/{name}")] public JsonResult Named(string name) { // query for all known list items var listable = RavenSession .Load<Listing>(String.Format("listings/{0}", name)) .Items .OrderBy(n => n.Order) .ToList(); return Json(listable, JsonRequestBehavior.AllowGet); }
So with this, I can plug this into anything that expects JSON data, for example in my situation, I am using Kendo UI
and their DropDownList
component with RemoteDataSource
, it is easy to make drop down lists bound to simple objects.
$("#genders").kendoDropDownList({ dataTextField: "Name", dataValueField: "Id", optionLabel: "Select ... ", dataSource: { dataType: "json", transport: { read: { url: "/list/named/genders" } } } });
Because I am dealing with simple JSON, and not Enums, this does everything I want. It gives me infinite list flexibility, it allows more verbose data that I may want in the future, and it provides a convenient model for repeating the process in multiple places.
The Question
I recently came under fire by a co-worker who saw this and claimed that it was "bad practice", and I should just use enums and deal with the problems of converting them all the time. To me, that seems troublesome and like it leaves so many gaps, and causes so many issues and leaves it so confusing. But their reasoning is that because enums are so much smaller in size, it will improve performance.
So I wanted to get the opinions of more people who may have experienced this. There are about 37+ lists in the entire program so far, and this is running great for me, and my control panel users love the flexibility. It has also made testing a lot simpler, and upgrading simpler.