How can I make something like a dynamic enumeration in C#? - c#

In an application I'm building I had an enumeration of account statuses:
public enum AccountStatus
{
Active = 1,
Trial = 2,
Canceled = 3
}
However, I needed more information from an AccountStatus so I made a class which has a few extra useful properties:
public class AccountStatus
{
public int Id {get; set;}
public string Description {get; set;}
public bool IsActive {get; set;}
public bool CanReactivate {get; set;}
}
This class get populated from a database table that might look like this:
1, "Active", True, True
2, "Trial", True, True
3, "ExpiredTrial", False, True
4, "Expelled", False, False
This is really handy when I have a customer object that uses the AccountStatus because I can write code like:
if(customer.Status.CanReactivate) // Show reactivation form
However, I have lost something equally important. I can no longer do this:
if(customer.Status == AccountStatus.Active) // allow some stuff to happen
What would be the best way, if its even possible, to include something that will allow me to mimic the enumeration within the class. I know that I could add public static fields to the AccountStatus class, but ultimately this doesn't work because if the database changes the code would have to be manually updated. By this, I mean:
public static readonly AccountStatus Active = new AccountStatus(1);
public static readonly AccountStatus Trial = new AccountStatus(2);
// etc, etc ...
I imagine there is probably a pattern for this somewhere, I just don't know what its called.
Any ideas?
CLARIFICATION
Based on the answers so far I need to clarify a couple of things.
The table above is a brief example. In my actual table there a many records, I have 12 in there right now. Plus we can add more or remove some existing. This is what I meant by "dynamic" in my question title.
Secondly, I gave a very simple use case for the ability I lost which apparently confused matters. Here is another real example:
if(customer.Status == AccountStatus.Trial || customer.Status == AccountStatus.ExpiredTrial)
... neither Trial nor ExpiredTrial are boolean values on the property. I don't want to add them either. That would set an even worse precedent than the one I'm trying to avoid (meaning I would have to add a new property to the class every time I added a new record to the table).
UPDATE
I selected an answer which didn't really meet was I was looking for, but suggests that I was looking for something unnecessary. After thinking about this, I concur. While adding an enum or static fields does duplicate some work (ie, having the values in both code and in a table) I think the benefits outweigh the negatives.

But why can't you use the enumeration as a property of that class..?
public enum State
{
Active = 1,
Trial = 2,
Canceled = 3
}
public class AccountStatus
{
public int Id {get; set;}
public State State {get; set;}
public string Description {get; set;}
public bool IsActive {get; set;}
public bool CanReactivate {get; set;}
}
And then:
if(customer.Status == AccountStatus.State.Active) // allow some stuff to happen

Rather than working with a strongly-typed enum, you could just do comparisons using a string:
public static readonly AccountStatus Active = new AccountStatus("Active");
or load the type from your database:
public static readonly AccountStatus Trial = new AccountStatus( reader["StatusField"] );
You can then do explicit comparisons:
if(customer.Status == "Active")
You lose the strong typing, but that's what dynamic means :-). You can store the known string values in constants to get some of this back.
edit
You could of course do this using the corresponding integer values, like you hinted at the end of your post. But strings are easier to read, and in this case using integers doesn't offer any sort of typing benefits.

I think you could achieve this by using a Flags enumeration where you can combine values:
[Flags]
public enum AccountStatus
{
Expelled = 1,
Active = 2,
CanReactivate = 4,
Canceled = 8,
Trial = Active | CanReactivate,
ExpiredTrial = CanReactivate,
}
However, it feels as if those different enum values move along different scales (some describe state, some describe valid actions), so it might not be the right solution. Perhaps you should instead split it into two enums.

I don't understand why you can't just write:
if (customer.Status.IsActive)

if you do/want something like this in your application:
if(customer.Status ==
AccountStatus.Active)
You have to know in your code that "Active" is a possible status. How else would you be able to write the actual word Active in your code. The status object can be dynamic, but the rest of the program that uses the status has to know what types of status exist in order to do something useful with it. What if active doesn't exist anymore, the status object may not need to be reimplemented, but the code that uses it does.
If every kind status is fully defined by parameters like it almost seems (active and trail have the same parameters, so more are needed to differentiate (expiration date?)), then check those parameters.
If a combination of parameters has to have a name, then make some kind of lookuptable where you can translate the name into its associated parameters, or its inverse. That way the name can be dynamic for that piece of code, and the parameter values are to upto some degree.
A possible real dynamic solution would be to implement some sort of scripting language/xml file/... that enables the user to specify the kinds of status, their parameters, and associate them with system behavior.

I still think your best bet is to add your missing cases to the class.
public class AccountStatus
{
public int Id {get; set;}
public string Description {get; set;}
public bool IsActive {get; set;}
public bool CanReactivate {get; set;}
public bool Trial {get; set;}
public bool ExpiredTrial {get; set;}
}
Which you can call in a simpler form than your example:
if(customer.AccountStatus.Trial || customer.AccountStatus.ExpiredTrial)
If you need to check a UserDefined status, expose that as a separate property:
public AccountStatusCode Status {get; set;}
...and call it like this:
if(customer.Status == AccountStatus.Active)
You can still add a constructor to it if you want to set an initial status.

You could make a many-to-many relationship between AccountStatus and Description. This way you can, at runtime, load all the different Descriptions you got, and then compare against those, using some sort of enumeration :)

This code does what you described in your post. I didn't code CanReactivate because you didn't say what the logic for that was.
public enum AccountStatusCode
{
Active = 1,
Trial = 2,
Canceled = 3
}
public class AccountStatus
{
private AccountStatusEnum status
//
// Constructor sets initial status.
public AccountStatus(int status)
{
this.Status = (AccountStatusCode)status;
}
public int Id { get; set; }
public string Description { get; set; }
//
//
public bool IsActive
{
get { return status == AccountStatusCode.Active; }
}
public bool CanReactivate { get; set; }
}
Note that, since you said you wanted to specify the initial account status as an int, I accept an int in the constructor, but then I cast it to an AccountStatusEnum for assigning it to the member variable. That's probably not the best practice...You should pass the constructor an AccountStatusCode value.

OK, well if you're working in C# 3.0, you could try extension methods:
// Define extension method like this:
public static bool IsActive(this AccountStatus status)
{
get { return status == AccountStatusCode.Active; }
}
//
// Call it like this:
if (AccountStatus.IsActive())
That keeps it out of your class.

I think what judo tried to explain is - a new status in the DB will require to put checking s for that new status in the conditional blocks. I think I am also doing something same. Only thing I am doing is I am also using another 'rank' field so that I can do range comparison instead of hard coding all the statuses.
For example instead of doing:
if(customer.Status == AccountStatus.Trial || customer.Status == AccountStatus.ExpiredTrial)
if I could put them in order I could do:
if(customer.Status < AccountStatus.Trial) as in our enum we can put them as ordered. So a new status in a new page wont break the other page's logics (depends on the rank of the status).

Related

Is there an approach in C# WebAPI with DTO's to only update elements that need to change?

In the tutorials I've walked through around creating an API in C#, I've gone through creating an HTTP PUT command for updating records in a table contained in a database.
The examples I've seen, essentially, I create a DTO around the fields that can be updated in that table. For example, I have a class that looks like the following:
public class UpdateTablenameDTO
{
public int ID { get; set; }
public int IsActive { get; set; }
public int IsDeleted { get; set;}
...
I then built a controller and all of the fields in my UpdateTablenameDTO appear as elements expected when I do an update.
What I wanted to know is there a proper approach to not requiring all of the elements in the Update DTO when doing the Update call? When I send my payload to include only ID and IsActive, it complained that it needed the rest of my fields. When I think this through, there could be a situation that a user is sitting on a screen with an old state but with a specific update that they want to send through (i.e. make the record inactive).
I don't necessarily want to update all of the elements, really only the specific changes, which would be the only thing I would want to send, along with the ID for identification. I suppose the way I could do this is to check if the record has changed since the user last viewed it upon updating, but I wanted to make sure I wasn't missing something obvious for this kind of scenario.
You can use Nullable value types to indicate that a property is "optional". The deserializer on the webapi side will keep the property as null when no value is provided. You can define the receiving DTO as follow:
public class UpdateTablenameDTO
{
public int ID { get; set; } // still required
public int? IsActive { get; set; } // now it's optional
public int? IsDeleted { get; set;} // optional as well
}
When you provide the JSON body as {"ID":5, "IsActive": 20} then you get an UpdateTablenameDTO instance as follow:
new UpdateTablenameDTO {
ID = 5,
IsActive = 20,
// IsDeleted = null
}
When you receive such an object, you can simply do a check against null or check the HasValue property to see, if there was a value in the request.
if (data.IsActive.HasValue) {
// do something with "data.IsActive.Value"
}

Managing multiple versions of object in JSON

I have a class in C#, that has a number of variables. Let's call it "QuestionItem".
I have a list of this object, which the user modifies, and then sends it via JSON serialization (with Newtonsoft JSON library) to the server.
To do so, I deserialize the objects that are already in the server, as a List<QuestionItem>, then add this new modified object to the list, and then serialize it back to the server.
In order to display this list of QuestionItems to the user, I deserialize the JSON as my object, and display it somewhere.
Now, the problem is - that I want to change this QuestionItem and add some variables to it.
But I can't send this NewQuestionItem to the server, because the items in the server are of type OldQuestionItem.
How do I merge these two types, or convert the old type to the new one, while the users with the old version will still be able to use the app?
You are using an Object Oriented Language, so you might aswell use inheritance if possible.
Assuming your old QuestionItem to be:
[JsonObject(MemberSerialization.OptOut)]
public class QuestionItem
{
[JsonConstructor]
public QuestionItem(int Id, int Variant)
{
this.Id = Id;
this.Variant = Variant;
}
public int Id { get; }
public int Variant { get; }
public string Name { get; set; }
}
you can extend it by creating a child class:
[JsonObject(MemberSerialization.OptOut)]
public class NewQuestionItem : QuestionItem
{
private DateTime _firstAccess;
[JsonConstructor]
public NewQuestionItem(int Id, int Variant, DateTime FirstAccess) : base(Id, Variant)
{
this.FirstAccess = FirstAccess;
}
public DateTime FirstAccess { get; }
}
Note that using anything different than the default constructor for a class requires you to use the [JsonConstructor] Attribute on this constructor and every argument of said constructor must be named exactly like the corresponding JSON properties. Otherwise you will get an exception, because there is no default constructor available.
Your WebAPI will now send serialized NewQuestionItems, which can be deserialized to QuestionItems. In fact: By default, JSON.NET as with most Json libraries, will deserialize it to any object if they have at least one property in common. Just make sure that any member of the object you want to serialize/desreialize can actually be serialized.
You can test the example above with the following three lines of code:
var newQuestionItem = new NewQuestionItem(1337, 42, DateTime.Now) {Name = "Hello World!"};
var jsonString = JsonConvert.SerializeObject(newQuestionItem);
var oldQuestionItem = JsonConvert.DeserializeObject<QuestionItem>(jsonString);
and simply looking at the property values of the oldQuestionItem in the debugger.
So, this is possible as long as your NewQuestionItem only adds properties to an object and does neither remove nor modify them.
If that is the case, then your objects are different and thus, requiring completely different objects with a different URI in your API, as long as you still need to maintain the old instance on the existing URI.
Which brings us to the general architecture:
The most clean and streamline approach to what you are trying to achieve is to properly version your API.
For the purpose of this link I am assuming an Asp.NET WebApi, since you are handling the JSON in C#/.NET. This allows different controller methods to be called upon different versions and thus, making structural changes the resources your API is providing depending on the time of the implementation. Other API will provide equal or at least similar features or they can be implemented manually.
Depending on the amount and size of the actual objects and potential complexity of the request- and resultsets it might also be worth looking into wrapping requests or responses with additional information. So instead of asking for an object of type T, you ask for an Object of type QueryResult<T> with it being defined along the lines of:
[JsonObject(MemberSerialization.OptOut)]
public class QueryResult<T>
{
[JsonConstructor]
public QueryResult(T Result, ResultState State,
Dictionary<string, string> AdditionalInformation)
{
this.Result = result;
this.State = state;
this.AdditionalInformation = AdditionalInformation;
}
public T Result { get; }
public ResultState State { get; }
public Dictionary<string, string> AdditionalInformation { get; }
}
public enum ResultState : byte
{
0 = Success,
1 = Obsolete,
2 = AuthenticationError,
4 = DatabaseError,
8 = ....
}
which will allow you to ship additional information, such as api version number, api version release, links to different API endpoints, error information without changing the object type, etc.
The alternative to using a wrapper with a custom header is to fully implement the HATEOAS constraint, which is also widely used. Both can, together with proper versioning, save you most of the trouble with API changes.
How about you wrapping your OldQuestionItem as a property of QuestionItem? For example:
public class NewQuestionItem
{
public OldQuestionItem OldItem { get; set; }
public string Property1 {get; set; }
public string Property2 {get; set; }
...
}
This way you can maintain the previous version of the item, yet define new information to be returned.
Koda
You can use something like
public class OldQuestionItem
{
public DateTime UploadTimeStamp {get; set;} //if less then DateTime.Now then it QuestionItem
public string Property1 {get; set; }
public string Property2 {get; set; }
...
public OldQuestionItem(NewQuestionItem newItem)
{
//logic to convert new in old
}
}
public class NewQuestionItem : OldQuestionItem
{
}
and use UploadTimeStamp as marker to understand, what Question is it.

Side effects on collection items or return a new collection?

Let's say I have a WriteItem class that looks like this:
public class WriteItem
{
public string Name { get; set; }
public object Value { get; set; }
public int ResultCode { get; set; }
public string ErrorMessage { get; set;}
}
I need to process each item and set its ResultCode and ErrorMessage properties and I though about defining a method similar to this:
public void ProcessItems(WriteItemCollection items)
{
foreach(var item in items)
{
// Process each item and set its result.
}
}
The processing of each item is done by another class.
Is this the best way to do it?
Or is it better to have the method return a collection of a custom Result class?
Both options have their advantages and disadvantages. Both are "fine" in the sense that there is nothing wrong with them and they are commonly used in C#.
Option 1 has the big advantage of being simple and easy. You can even keep a reference to a WriteItem instance and check its status after processing.
Option 2 has a clearer separation of concerns: In Option 1, you need to add comments to your WriteItem class to define which are "input" and which are "output" properties. Option 2 does not need that. In addition, Option 2 allows you to make WriteItem and ProcessingResult immutable, which is a nice property.
Option 2 is also more extensible: If you want to process something else than WriteItems (with the same return options), you can define a class
class ProcessingResult<T>
{
public T Item { get; set; }
public int ResultCode { get; set; }
public string ErrorMessage { get; set; }
}
and use it as ProcessingResult<WriteItem> as well as ProcessingResult<SomeOtherItem>.
What you wrote will work. You can modify the object properties without having side effects while iterating in the collection.
I wouldn't return a new collection unless you need to keep a copy of the original collection untouched.
I think it all comes down to readability.
When you call ProcessItems, is it obvious that the collection has changed? If you call the method like this:
var items = GetItemsFromSomewhere();
ProcessItems(items);
versus calling it like this:
var items = GetItemsFromSomewhere();
items = ProcessItems(items);
or simply changing your methodname:
var items = GetItemsFromSomewhere();
items = UpdateItemStatuses(items);
In the end there is no right answer to this question in my book. You should do what feels right for your application. And consider: what if another developer was looking at this piece of code? Can he surmise what is happening here, or would he have to dive into the ProcessItems-function to get the gist of the application.
It is better to return a new results class.
why?
As others have said you are modifying the collection and its not really clear. But for me this is not the main reason. You can have processes which modify objects.
For me its because you have had to add extra properties to your WriteItem object in order to support the processor. This in effect creates a strong coupling between the model and a processor which should not exist.
Consider you have another method ProcessItems_ForSomeOtherPurpose(List<WriteItem> items) do you expand your ResultCode int to have more meaningful values? do you add another property ResultCode_ForSomeOtherPurpose? what if you need to process the same item mutiple times with multiple processors?
I would give your Model an Id. Then you can log multiple processes against it
eg.
item 1 - loaded
item 1 - picking failed!
item 1 - picked
item 1 - delivered

C# Complex Property Setter option

I have an ASP.Net MVC 5 (C#) application and I'm giving my users the ability to like posts and comments.
for this I have a Model called Likes with the following Properties:
public class Like
{
public Like()
{
this.CreatedUTC = System.DateTime.UtcNow;
this.isActive = true;
}
public long id { get; set; }
public string UserID { get; set; }
public bool isActive { get; set; }
public LikeType Type { get; set; }
public DateTime CreatedUTC { get; set; }
}
Type is an enum and it can be either Comments or Posts. I've also added the following Navigation Property to both Post Model and Comment Model:
public virtual ICollection<Like> Likes { get; set; }
My question is, can I have a setter function in the above line of code where it would automatically set Comments and Posts for the Like type? I know I can use the Value variable in the Setter but using Google I couldn't find how to use it for complex types as I have above (Like).
I'm sure this would be a better way of doing this than in the repository manually set that enum every-time I'm going to save a like.
UPDATE:
Seeing how we're starting a nice little conversation about this Questions, I will throw another alternative in the mix.
How about two Tables with a base class of Like, one CommentLikes and the other PostLikes Seeing that the size of this table will grow really fast, I'm thinking separating them would possibly be a good thing, Right?
I'd rather drop the "LikeType" and either have Comment and Post entity in the Like entity and distinguish by one of them being null or introduce two new entities
public class CommentLike
{
public Comment Comment { get; set; }
public Like Like { get; set; }
}
which holds a comment and a like and PostLike which holds a Post and a Like. The Comment then looks like this
public Comment
{
public virtual ICollection<CommentLike> { get; set; }
}
Another alternative is to create separate tables for comment and post likes. While what you ask for is definitely doable I would recommend more verbose but simpler solution which will result in code that is easier to maintain and has less bugs. Is there any specific reason you want to have a LikeType property?
I had same problem but didnt encounter an easy way.
class Post{
public virtual ICollection<Like> Likes {set;get;}
}
class Comment{
public virtual ICollection<Like> Likes {set;get;}
}
Then:
class Like{
//....
}
You dont need a bi-directional relationship. Do you have a case where you need to query Likes table? and if you do, you will have to manage parsing it as ENUM somewhere which can be an extension method.
EF will create Post_Id and Comment_Id implicityly in your table design. You wont be able to query it but you wont need to. IN my experience i never needed to.
My question is, can I have a setter function in the above line of code
where it would automatically set Comments and Posts for the Like type?
I assume you are using T4 template or the classes that were generated by EF are partialthen you can extend it by creating another partial class and a wrapper property for Likes
// Backing field
private ICollection<Like> _likesWrapper;
public ICollection<Like> LikesWrapper {
get {
// Lazy loading
if(_likes == null)
{
// Just create a new list or load it from the database.
_likes = new List<Like>();
}
return _likes;
}
set {
foreach(var val in value)
{
//Set the comment and posts
}
Likes = value;
}

Architecture for achievements / badges

There's a lot of questions already about coding a badge system similar to SO, my question is different. Assume I have a web page system, badges / achievements, stored in the DB as a row with the achievement key (id), user id, and any other data.
My simple question is, where should I store the badge ID? I have one class per achievement with all the data and methods for testing if it has been earned. I imagine I may have dozens or hundreds at some point. I want the IDs to be used hard coded only once, and in one concise place, so I have no chance of accidentally changing them or mixing them up.
I could hard code them in the class, like
public int Key { get { return 15; } } // I'm calling it Key, not ID
but if I split my achievements among multiple files I don't want to have to run around looking for the highest Key when I add a new one and risk a mistake.
I could put them in some dictionary in another class...
public class AchievementSet
{
private Dictionary<int, Achievement> _achievements;
public AchievementSet()
{
_achievements = new Dictionary<int, Achievement>()
{
{ 1, new SomethingAchievement() }
};
}
}
But now the class itself doesn't know its own key, and it needs to (or does it?) If I passed it into the constructor now I risk mismatching the numbers.
Any recommendations?
In the context of Stack Overflow, I'd imagine each badge has properties such as:
Id, Name, Class (Bronze, Silver or Gold) and Description etc.
You mention that you currently have a class for each badge/achievement, each with appropriate checks for conditions on which it would be awarded.
The reason I'm suggesting you move away from the model you're looking at now (one class per achievement) is because you're going to continue to face huge problems down the road when you're navigating through 200 different classes looking for that one ID you can't recall.
By storing your badges in the table, your data is all in one logical place and not scattered across your application.
In answer to the question: So do you disagree with the accepted answer to: stackoverflow.com/questions/3162446/
Not necessarily, and I like this idea more than my earlier proposal for a single class that would check all the badges based on their ID.
Despite its name, I believe RexM is not defining the CommenterBadge itself in that file and should have named it CommenterBadgeJob. (You'll notice it has none of the traits I've defined in my answer and inherits from BadgeJob). The obvious question is "How does each badge job know which BadgeId it corresponds to?"
I would have an additional unique field in my Badge called BadgeJob by which you could lookup a badge.
enum BadgeClass {Bronze, Silver, Gold}
//This class would be inherited from the database.
public class Badge
{
public int Key {get;set;}
public string Name {get;set;}
public BadgeClass Class {get;set;}
public string BadgeJob {get;set;}
public string Description {get;set}
}
I would modify his code as follows:
public class CommenterBadgeJob : BadgeJob
{
public Badge commenter_badge {get;set;}
public CommenterBadgeJob() : base()
{
//Lookup badge
string badge_job_name = this.GetType().Name;
commenter_badge = db.Badges.Where(n=>n.BadgeJob == badge_job_name).Single();
}
protected override void AwardBadges()
{
//select all users who have more than x comments
//and dont have the commenter badge
//add badges
}
//run every 10 minutes
protected override TimeSpan Interval
{
get { return new TimeSpan(0,10,0); }
}
}
How about using an enum ?
public enum BadgeTypes
{
GoodAnswer = 1,
Commenter = 2,
Teacher = 3,
//...
}
Each BadgeJob could have a BadgeType property which would be used to populate the badge id when inserting an achievement during AwardBadges() (enum values can be persisted to integers).
I see no necessity for having one class per achievement. BadgeJob's contain all badge attribution logic and BadgeTypes suffice to represent the different badges.

Categories

Resources