Enum properties, Web API, JSON deserialization and typos - c#

Let's say we got a class like this one:
public class Person
{
[JsonConstructor]
public Person(string name, DayOfWeek bornOnDay) => (Name, BornOnDay) = (name, bornOnDay);
public string Name { get; protected set; }
public DayOfWeek BornOnDay { get; protected set; }
}
And an endpoint like this one:
[HttpPost]
[Route("api/people")]
public IHttpActionResult PostPerson([FromBody]List<Person> people)
{
// whatever
}
I've noticed that if I make a typo in enum type when making a request - no error occurs, only the object that has the erroneous enum won't be deserialized.
For instance, the request body may look like this:
[{"name":"John", "bornOnDay":"Moonday",},
{"name":"Mark", "bornOnDay":"Friday",},]
Endpoint will receive the list containing one Person, Mark, born on Friday.
I would like to make the post operation either happen completely, or fail, not with only with the objects without enum typos.. Is there a way to do it, without receiving a string instead of DayOfWeek, and then using Enum.TryParse(...) to determine that the input was erroneous?
Edit:
I actually want the error to occur, and be detectable, so I can return 400 code to the client.

you can do like this , you can create enum value from string you have inputted
public class Person
{
private string day;
[JsonConstructor]
public Person(string name, string bornOnDay){
this.Name = name;
this.day = bornOnDay;
}
public string Name { get; protected set; }
public DayOfWeek BornOnDay {
get {
DayOfWeek weekday;
if(Enum.TryParse(day, true, out weekday))
return weekday;
else
return DayOfWeek.None;//add none if no able to parse
}
}
Note : None is added according to Null Object Pattern to avoid issues related to null value in system.

What about this:
public class Person
{
[JsonProperty]
public string Name { get; protected set; }
[JsonProperty]
private string _BornOnDay { set {
//try parse the string, if not successful, throw a nicely
//formatted error with the original string and what you expect,
//if parse is successful, set the value to BornOnDay;
}}
[JsonIgnore]
public DayOfWeek BornOnDay { get; protected set; }
}
Not sure about the JsonConstructor attribute, never needed to use it. If you do need it and if I understand it correctly you can have something similar with it. Deserializer should give a string into constructor, and then inside of it, you do parsing logic for the enum yourself.

Related

Parser for multiple data types without returning object

I'm building a parser for some data that's given as XML, something similar to:
Get file in path %windir%\system32\calc.exe and retrieve it's CreationTime
The small problem that I have is that the type of object that I'm retrieving (FileInfo in example above) and the data type of the property I'm reading (CreationTime which is DateTime in example above) isn't always the same.
For example: on a FileInfo object alone I could be asked for:
bool Exists
DateTime CreationTime
DateTime LastWriteTime
long Size
Version Version
Other object types could be things like FolderInfo, RegistryKey and RegistryValue
With that in mind, I created the following code:
public interface IPropertyRetriever<out T>
{
public string Name { get; }
public Property Property { get; }
public T RetrieveProperty();
}
public enum Property
{
Count,
DateCreated,
DateModified,
RegistryKeyExists,
RegistryValueExists,
Size,
Value,
Version
}
public class FilePropertyRetriever<T> : IPropertyRetriever<T>
{
public FilePropertyRetriever(string name, Property property, string path, bool is64Bit)
{
Name = name;
Property = property;
Path = path;
Is64Bit = is64Bit;
}
public string Name { get; }
public Property Property { get; }
public string Path { get; }
public T RetrieveProperty()
{
var file = ...
// Do something to retrieve FileInfo,
// assumes if it got to code below FileInfo.Exists is true
return (T) (object) (Property switch
{
Property.Count => file.Exists,
Property.DateCreated => file.CreationTime,
Property.DateModified => file.LastWriteTime,
Property.Size => file.Length,
Property.Version => Version.TryParse(FileVersionInfo.GetVersionInfo(Path).ProductVersion,
out var version)
? version
: null
});
}
}
I know that my T RetrieverProperty() method isn't exactly very good programming - I'm telling my method what type I want it to return when in fact it knows already and using generics to cast to the correct type (and boxing it first if DateTime/long/int), but I really can't think of a better way of doing this.
Any suggestions on how to improve this?
PS: The reason why the RetrieveProperty() accepts no parameters and instead uses properties is because the device where the object is created and where the method are run is not the same, the object is serialised and sent over.
why can't IPropertyRetriever just be this:
public interface IPropertyRetriever
{
public string Name { get; }
public int Count {get;}
public DateTime DateCreated {get;}
public DateTime DateModified {get;}
public bool RegistryKeyExists {get;}
public bool RegistryValueExists {get;}
public long Size {get;}
//etc
}
And call it something different IFileInformation. Or have different interfaces returned for different objects with a base interface as not all the above properties are relevant to all objects.

Get propertyname where attribute is on

In my application I have a custom attribute calles ResourceTargetAttribute which looks like:
[AttributeUsage(AttributeTargets.Property)]
private class ResourceTargetAttribute : Attribute
{
public ResourceTargetAttribute(string resourceKey)
{
ResourceKey = resourceKey;
}
public string ResourceKey { get; private set; }
}
The usage looks like:
[ResourceTarget("FileNotFoundErrorText")
public string FileNotFoundErrorText { get; private set; }
The constructor of the class where the FileNotFoundErrorText-Property is defined resolves this attribute. This just works fine.
Now I was thinking about to extend the attribute to have a parameterless constructor and if this is called the name of the Property the attribute is on will automatically be used for the ResourceKey.
Therefore I've introduced a new constructor which just looks like:
public ResourceTargetAttribute()
{
}
And the usage then should look like:
[ResourceTarget()]
public string FileNotFoundErrorText { get; private set; }
And here I want to have name of the FileNotFoundErrorText-Property automatically be passed to the ResourceTarget-Attribute.
Is there a possibility to do this?
The CallerMemberNameAttribute might help you:
public ResourceTargetAttribute([CallerMemberName] string propertyName = null)
{
ResourceKey = propertyName;
}
Usage:
[ResourceTarget]
public string FileNotFoundErrorText { get; private set; }
If you get the attribute, the
attr.ResourceKey
property should contain FileNotFoundErrorText as value.
Otherwise I just would go the way passing the name as string as attributes are metadata applied to the members of a type, the type itself, method parameters or the assembly so you must have the original member itself to access its meta data.
The easiest way would be to utilize nameof-operator:
[ResourceTarget(nameof(FileNotFoundErrorText)]
public string FileNotFoundErrorText { get; private set; }
Another approach would be to modify the code that actual examines / searches for these marker-attributes. use reflection to get the actual Property-Name on which the attribute was applied.
Maybe if you provide the mentioned "constructor-code" I could further assist.

I want to fill class members with default if they are missing, using generic in C#

I have multiple web requests that post JSON object and I have serializable classes with all the fields. For example:
[Serializable]
public class RequestOne()
{
public string date;
public string information;
public string subject;
}
[Serializable]
public class RequestTwo()
{
public int ID;
public string Data;
public string message;
}
And my method takes partially filled request class and I want to fill in any missing fields with default values declared in constant class.
And I want to avoid writing each method with for each request, like :
public static void FillWithDefault(this RequestOne request)
{ if (request.date.Equals(null)) request.date = DEFAULT_DATE;
if (request.information.Equals(null)) request.information = DEFAULT_INFO;
if (request.subject.Equals(null)) request.subject = DEFAULT_SUBJECT;
}
public static void FillWithDefault(this RequestTwo request)
{
//do the same for the fields in RequestTwo
}
I want to know if there is any way to achieve this using generic?
I want to do something similar to this:
public static void FillWithDefault<T>(this T request)
{
if(typeof(T) == typeof(request))
{
//check each member in request and fill with default if it's null
}
.
.
.
}
So that in my main method I can use like this :
RequestOne request = new RequestOne();
request.FillWithDefault();
RequestTwo request2 = new RequestTwo();
request2.FillWithDefault();
Can someone please help with idea on this? Am I overthinking on this? I'm new to generic so please feel free to advise on my code.
Edit
Sorry guys, I did not mention that I will be using this method for test automation. Those request contracts cannot be changed since it's by design. Sorry again for the confusion!
Use constructors. Also make use of properties. Don't gather the default filling code to one place, it's the responsibility of the classes so keep them there.
[Serializable]
public class RequestOne()
{
public string date { get; set; };
public string information { get; set; };
public string subject { get; set; };
public RequestOne()
{
Date = DEFAULT_DATE;
Information = DEFAULT_DATE;
Subject = DEFAULT_SUBJECT;
}
}
[Serializable]
public class RequestTwo()
{
public int ID { get; set; };
public string Data { get; set; };
public string Message { get; set; };
public RequestTwo()
{
Data = DEFAULT_DATA;
message = DEFAULT_MESSAGE;
}
}
Generics are used when the types have common operations/properties defined so you can apply the same routine for each type in one place instead of declaring different methods for each type.
However in this case, you have two different types with different properties, so I would not use generics here. You can achieve it with manual type checking and using reflection to get properties and set them but it's not a good way and definitely wouldn't be a good usage of generics.
Overloading is the way to go.
you can use property
[Serializable]
public class RequestOne()
{
private string _date;
public string date { get { return _date;} set { _date = value ?? DEFAULT_DATE; }};
public string information; // same here
public string subject; //same here
}

Formatted string as datatype

I have a format for certain Ids and I'd rather have a custom datatype for them rather than store them as a string.
How is this done in C#?
Is this a good idea in the first place?
An example below should explain what I mean:
Id format (D for digit, C for alphabet char): CCDDDD
public ItemId id { get; set; }
...
public class ItemId {
// somehow declare the format here
}
You could wrap a class around your string ID which takes a input string as constructor parameter. This way you can also put methods in your class to provide extra functionality etc, and always have the formatting in one place. Simple example:
public class ItemId
{
private string _id;
public string ID
{
get { return _value; }
set
{
//do some formatting here
_id= value;
}
}
public ItemId(string id)
{
ID = id
}
public override string ToString()
{
//do some extra formatting here if needed
return Value;
}
}
Because you can only manipulate the real ID through the public setter, you can have your formatting and validation logic in one single place. Hope this helps you a bit. I think it's a good idea because a class ItemId is more meaningful then just a string, and also a lot easier to extend or change functionality in the future.
For example you can check the input with regex, and throw your own exception if input does not match your format. That gives you meaningful information at runtime. Also you can add xml comment to the public setter, so if you or anyone uses it, it's clear what the ID should look like.
You can also do it by implementing your extensions, which I would prefer than overriding ToString method.
For Example
public static class Extension
{
public static string MyFormat(this string str)
{
//do sth with your string
return str;
}
}
And you can use this extension like
string abc = "";
abc.MyFormat();

How to implement a DateTime entity property to set always DateTime.Now

I've read a bunch about auto implemented properties but I still don't quite get it. I have and entity:
public class News
{
public int NewsId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime Date { get; set; }
}
Now I don't want the user to set date himself every time a new entity of News type is created. I want the record to be saved automatically with the datetime it's created. Thinking about it I suggest that it's enough to just modify the set for my property to something like :
public DateTime Date
{
get;
set
{
Date = DateTime.Now;
}
}
But reading about the topic I saw that the standard way is to create private variable and use it instead in the implementation. That's where I get a little bit lost.
private DateTime _date = null;
public DateTime Date
{
Well I'm not sure for the getter and setter implementations. It seems reasonable to have something like : set { _date = DateTime.Now;} and I have no idea how to deal with the get part since I want this data to be fetched from the database so something like : get {return _date;} doesn't make much sense to me even though almost every example with auto implementedset` returns the private variable. But I think that if the property is an entity this is not making a lot of sense.
Some ways to return the current date:
public DateTime Date { get { return DateTime.Now; } }
or
public class News
{
public News()
{
Date = DateTime.Now;
}
public DateTime Date { get; private set; }
}
The first one will always return the current date/time, even if that instance was created some time ago. The second one will return the date/time the instance was created. Both prevent the user from setting that Date value.
You could add a constructor to your class and then initialize there your property.
public class News
{
// properties goes here
public News()
{
Date=DateTime.Now;
}
}
A far better constructor would be the following
public News(int newsId, string title, string content)
{
NewsId=newsId;
Title=title;
Content=content;
Date=DateTime.Now;
}
That way you could create an object of type News in a single line of code.
News news = new News(1,"title1","whatever");
Don't touch the getter and setter! They are auto generated from a template and will be overridden every once and a while. Instead, as you might have noticed the generated entities are declared partially, create a partial class and declare a constructor there that sets the _date or Date of you r entity to DateTime.Now on construction (just as you desired).
public partial class News
{
public News()
{
this.Date = DateTime.Now;
}
}

Categories