We're developing a web application using ASP.Net MVC 5 C#, I'm having difficulties coming up with a nice solution for my problem.
We're accessing an api that has a few enums coming across as text (i.e. "yes", "no", "notfound", "na") on the model itself I'd like to have strongly typed enums that store in the database as an integer (save a little space I think) the issue comes when we're deserializing the response from the api. In the response it will comes across as text (see above) where as the enums will be expecting an integer.
How is this done? I really hate asking questions where I haven't tried anything but my web searches hasn't resulted in anything. If you needed any code please let me know and I'll update the question with the segments needed. As of right now the model has several strongly typed enums and the api is returning strings of the enum values. by the way The enum texts are the same as the return values.
In-case it makes any difference we're also using EF Code First 6
You can use Enum.TryParse to convert the string to its enum value
public enum MyEnum
{
NA
Yes,
No
}
then
MyEnum value = MyEnum.NA;
Enum.TryParse<MyEnum>(theTextValue, out value );
Related
I want my api to accept a query string array parameter in the form ?id=a,b,c,d as opposed to the standard ?id=a&id=b&id=c. For this I am following this excellent blog article.
Side question: Does ASP.NET Core provide built-in providers/attributes for that yet?
My main question however, is about SwashBuckle.AspNetCore:
In short, I'm trying to get a comma-separated parameter (?p=a,b,c,d) that is typed as an IEnumerable<string>, to show up correctly in the generated Swagger. According to the specs it should get style=form and explode=false. The default here (style=form and explode=true) matches the asp.net core default. How do I achieve this? Is there an annotation attribute that I can place on the parameter?
I'm looking to use Swashbuckle to generate Swagger docs and Swagger UI for a Web API method that looks like the following (it's effectively a mail-merge with PDFs):
[HttpPost]
[ResponseType(typeof(byte[]))]
public HttpResponseMessage MergeValues([FromUri]Dictionary<string, string> mergeValues,
[FromBody]byte[] pdfTemplate)
{
return MergeStuff();
}
This isn't currently working, or at least I'm not sure how to interact with the resulting Swagger UI.
This creates the following Swagger UI, which seems reasonable, but I'm not sure what to do with it to populate it correctly (or if it's even correct). I'm using pretty much all default Swashbuckle settings from the latest Nuget.
Byte Array: If I enter Base64-encoded text for the byte array, the byte array always shows up null. Turns out I just need my BASE64 text surrounded by double-quotes and then it works.
Dictionary: I've tried various types of JSON expressions (arrays, objects, etc) and am unable to get any of the values in the Dictionary to populate (the resulting Dictionary object has 0 items).
I have the ability to change things and would like to know how I can do this. For example, if changing the dictionary to an array of KeyValuePair<string,string> helps, let's do it.
Options I know that I have that I'd like to avoid:
Changing these input types to strings and doing my own manual deserialization/decoding. I'd like to be explicit with my types and not get too fancy.
Custom binders. I'd like to utilize standard/default binders so I have less code/complexity to maintain.
My question really was a two-parter. Here are the answers, although only a partial answer to the second question:
Question 1: How do I fill in the data for a byte array?
Answer 1: Paste in your base64-encoded value for it but be sure to surround that content with double-quotes, both at the beginning and end.
Question 2: How do I fill in the data for the Dictionary?
Answer 2: While it doesn't work with [FromUri], this will work with [FromBody] (either Dictionary or IDictionary<string,string> will work):
{"FirstName":"John","LastName":"Doe"}
I'm not sure why this doesn't work with FromUri but I'm going to ask a separate question that's much more focused than this one to get to the bottom of that. For the purposes of this question, both parameters can be put into a DTO, flagged as [FromBody], and all is good then.
I have a RESTful Web API project, and I have 2 different Enum scenarios that I'm unsure of re best practice.
Scenario 1 : Straightforward Enum Param
My API method requires a parameter called ruleType, with valid values being EmailAddress and IPAddress.
My enum within the Web API project looks like this:
public enum RuleType
{
None = 0,
EmailAddress = 1,
IPAddress = 2
}
My question for this scenario is, should I use ?ruleType=EmailAddress in my request to the API (which automatically binds that value to my RuleType property within the API method)? If so, how best to validate that the RuleType param sent, is a valid RuleType Enum value?
Scenario 2 : Multiple Enum Values for a Single Param
My API method has an optional fields param, which is allows you to specify any additional data that should be returned. E.g. &fields=ruleOwner,rule. This would return those 2 extra bits of data in the response.
I have an enum in the Web API project which relates to each possible field that may be requested, and at present, I am splitting the comma separated fields param, then looping through each string representation of that enum, parsing it to the equivalent enum, resulting in a list of Enum values which I can then use within my API to retrieve the relevant data.
This is the Enum:
public enum OptionalField
{
None = 0,
RuleOwner = 1,
Rule = 2,
etc.
}
What would be best practice here? I was looking into bitwise enums, so a single value is sent in the API request which resulted in any combination of fields but didn't know if this would work well with a Web API, or if there's generally a better way to go about this?
The simplest answer is, "It doesn't matter".
If the parameter in your controller method is of the enumeration type
public IHttpActionResult Foo(RuleType ruleType)
In WebAPI, It Just Works - no matter if the client request URL specifies the parmeter value as ?ruleType=1 or ?ruleType=EmailAddress
If the client specifies a value that isn't valid for the enumeration, an exception is thrown (The parameters dictionary contains a null entry for parameter 'ruleType' of non-nullable type 'RuleType' for method 'Foo' ... and the client gets a 400 Bad Request response.
it is a best practice to make the URI "human readable". so i can also understand why you using Enum as a string. But as HristoKolev said you have to write a custom Model Binder.
In fields i think you should not use an combination of enums. because it is difficult to understand. perhaps you can create an combination of enum as enum entry
public enum OptionalField
{
None = 0,
RuleOwner = 1,
Rule = 2,
RuleAndRuleOwner = 3,
etc.
}
For scenario 2 there is built in support in C# for Bitmask operations in Enums using the [Flags] attribute
[Flags]
public enum OptionalField
{
None = 0,
RuleOwner = 1,
Rule = 2,
RuleAdministrator = 4,
RuleEditor = 8,
...etc
}
Which is described in this SO post
As Christian already stated in his answer it's probably not good practice to use this in a REST API but it should work.
Multiple values or not, if you are using an Enum as a string, you have to parse it manually. In .NET the Enums are integers so if you want to send an enum to the default model binder you have to do it like this: ?ruleType=1.
You can write your own model binder that will accept string, but you have to ask yourself why are we doing it? If you want the user to be able to easily identify the url then use strings. If not there is no reason not to use integers. As you said, you can use FlagsAttribute to combine multiple values.
In .Net Core you can add below statement in ConfigureServices() method in StartUp.cs or Program.cs bepending on what version of .NET Core you are using.
services.AddControllers()
.AddJsonOptions(opt=> { opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); });
I am experiencing a strange behavior with very basic web service development. This question might be dumb but I think someone would be able to explain this observation.
I am developing a web service with a web method, MyWebMethod
MyWebMethod(MyEnum Param, .....)
Where,
public enum MyEnum : int
{
Type_1 =1;
Type_2 =2;
Type_3 =3;
}
Now I am using my client to communicate with this service but for every request type, Type_1, Type_2 etc the service captures it as Type_1. As an example, if I create a break point at MyWebMethod in my web service, I see Type_1 as param1 type. I guess this is a problem with Namespacing. I cannot see any other defects on the code. Any Idea based on the experiences?
When enum is serialized, only its string representation is transferred through wire (names), not the values. I believe thats the reason you are getting the wrong values.
Check out this 2 articles for more info
WebServices_and_Enums
Using enum in web service parameter
If this is a WCF web service and a .NET 2.0 client generated with wsdl.exe for each value type in the method signature there will be a boolean parameter added called XXXSpecified which you need to set to true. Check this blog post for more details.
I guess your enum does not need to inherit from int. You are providing name and value in the enumeration, that should suffice. I am assuming all your code is .NET 2.0. As test , return an enumeration value from the webservice. Just to make sure XML Serialization is working as expected when the service is hit directly by the browser.
I have a table in my database called "OrderItemType" which has about 5 records for the different OrderItemTypes in my system. Each OrderItem contains an OrderItemType, and this gives me referential integrity. In my middletier code, I also have an enum which matches the values in this table so that I can have business logic for the different types.
My dev manager says he hates it when people do this, and I am not exactly sure why. Is there a better practice I should be following?
I do this all the time and I see nothing wrong with this. The fact of the matter is, there are values that are special to your application and your code needs to react differently to those values. Would your manager rather you hard-code an Int or a GUID to identify the Type? Or would he rather you derive a special object from OrderItem for each different Type in the database? Both of those suck much worse than an enum.
I don't see any problem in having enum values stored in the database, this actually prevents your code from dealing with invalid code types. After I started doing this I started to have fewer problems, actually. Does your manager offer any rationale for his hatred?
We do this, too. In our database we have an Int column that we map to an Enum value in the code.
If you have a real business concern for each of the specific types, then I would keep the enum and ditch it in the database.
The reason behind this approach is simple:
Every time you add an OrderType, you're going to have to add business logic for it. So that justifies it being in your business domain somewhere (whether its an enum or not). However, in this case having it in the database doesn't do anything for you.
I have seen this done for performance reasons but I think that using a caching mechanism would be perferable in most cases.
One alternative to help with the synchronization of the database values and the business logic enum values would be to use the EnumBuilder class to dynamically generate a .dll containing the current enum values from the database. Your business logic could then reference it, and have intellisense-supported synchonized enum values.
It's actually much less complicated than it sounds.
Here's a link to MSDN to explain how to dynamically build the enum.
http://msdn.microsoft.com/en-us/library/system.reflection.emit.enumbuilder.aspx
You just have to sub in the database access code to grab the enum values:
One more vote for you, I also use mapping database int <-> application enum, in addition, I usually describe my enums like this:
public enum Operation
{
[Description("Add item")]
AddItem = 0,
[Description("Remove item")]
RemoveItem = 1
}
which leaves me absolutely free to add new values without need to change database and with a very short workaround I can work i.e. with lists containing descriptions (that are very strongly tied to values!) - just a little bit of reflection reaches the goal!
In code, you can typically just add a property like this:
public class Order
{
public int OrderTypeInt;
public OrderTypeEnum OrderType
{
get { return (OrderTypeEnum)OrderTypeInt; }
set { OrderTypeInt = (int)value; }
}
}