How to determine if a property is required? - c#

Given a class something like this:
public class MyClass : ValidationValues
{
public string Foo { get; set; }
[Required(ErrorMessage = "Bar is required.")]
public string Bar { get; set; }
// and many more
}
public class ValidationValues
{
public bool IsValid { get; set; } = true;
public string InvalidReason { get; set; }
}
I need to determine if a property is required while looping over it as a generic list. By looking into the Watch, I've figured out one way, but it feels clunky, and I'm thinking it should be simpler.
For some context, this logic is inside of an Azure Function. So no Views, no MVC, etc. The function is a Blob Storage trigger that picks up a .CSV file with a | delimited list which gets deserialized into a List<MyClass>. We do not want to enforce the Required attributes at deserialization because we want more granular control.
So given a file like this:
value1 | |
value2 | something
What eventually gets sent back to the user is something like this:
[
{
"foo": "value1",
"bar": "",
"isValid": false,
"InvalidReason" : "Bar is required"
},
{
"foo": "value2",
"bar": "something",
"isValid": true,
"InvalidReason" : ""
}
]
Here's what I have now:
foreach (T item in itemList) // where 'itemList' is a List<T> and in this case T is MyClass
{
foreach (PropertyInfo property in item.GetType().GetProperties())
{
if (property.CustomAttributes.ToList()[0].AttributeType.Name == "RequiredAttribute")
{
// validate, log, populate ValidationValues
}
}
}
This is the part I don't like:
property.CustomAttributes.ToList()[0].AttributeType.Name == "RequiredAttribute"
Sometimes when I figure out a coding challenge, I tell myself, "This is the way". But in this case, I'm pretty sure this isn't the way.

You can rewrite that line using GetCustomAttibute:
using System.Reflection;
foreach (T item in itemList) // where 'itemList' is a List<T> and in this case T is MyClass
{
foreach (PropertyInfo property in item.GetType().GetProperties())
{
var attribute = property.GetCustomAttibute<RequiredAttribute>();
}
}

Reflection is slow - or at least, relatively slow. So; the main important thing here is: don't do this per instance; you could either cache it per Type (from GetType(), or you could just use T and never even check .GetType() per instance, depending on your intent. This includes caching the properties that exist for a given type, and which are required. However, the real bonus points are to use meta-programming to emit - either at runtime, or at build-time via a "generator" - a method that *does exactly what you want, without any loops, tests, etc; i.e. in this case it might emit a method that does the equivalent of
void ValidateMyClass(MyClass obj)
{
if (string.IsNullOrWhitespace(obj.Bar))
{
DoSomething("Bar is required.");
}
}
This can be done in a variety of ways, including the Expression API, the emit API (ILGenerator), emitting C# and using CSharpCodeProvider, or the "generators" API.

Related

Json Serialization/Contract Resolvers - Need to serialize object so contained list of key value pairs is converted to name value pairs at same level

Since I'm having a hard time explain my question, I made a fiddle.
https://dotnetfiddle.net/aa18qT
I have a class that contains a list of another class with only two properties a key and a value. Alternatively I could use a dictionary. I am serializing this to json.
Currently I have
{
"part":1,
"quant":2,
"comments":"something",
"CustomFields": [
{
"key":"groc1",
"value":"truffles"
},
{
"key":"groc 2",
"value":"some thing"
}
]
}
And what I want is
{
"part":1,
"quant":2,
"comments":"something",
"groc 1": "truffles",
"groc 2":"some thing"
}
Where groc 1 and groc 2 are two of the key value pairs. The names (groc 1) are not known at compile time and could be anything in any amount. They need to be passed as parameters to another external system with those unknown fields as the name/key.
Trying to figure out how best to do this. I've been fiddling with contract resolvers and converters but not getting anywhere..maybe something with expando objects and reflection? But seems like someone must have run into this before so maybe there is an easier way all my googling hasn't found yet. Seems like it should be simple but I've been staring at it too long.
I can change the structure or type of the child object, but output needs to be like above.
Any suggestions?
you can try this
var newObj=JObject.Parse(json);
var customFields=(JArray) newObj["CustomFields"];
newObj.Properties().Where(p=> p.Name=="CustomFields").First().Remove();
foreach (JObject item in customFields)
newObj.Add((string) item["key"],item["value"]);
json=newObj.ToString();
json
{
"part": 1,
"quant": 2,
"comments": "something",
"groc 1": "truffles",
"groc 2": "some thing"
}
but if you need c# classes, you have to create a custom class using a Dictionary as JsonExtensionData
Item newItem=newObj.ToObject<Item>();
public class Item
{
public int part { get; set; }
public int quant { get; set; }
public string comments { get; set; }
[JsonExtensionData]
public IDictionary<string, object> CustomFields { get; set; }
}

DESerialize JSON to c# objects dynamically

I am getting JSON data from a webservice. it is providing me with FORM DATA with different questions and answers. every answer is a different c# object. I am trying to find the best way to map the ANSWERS to correct c# object.
for example if Question Id is "37" Then its a Address Object.
I have JSON String like in this format below
"answers": {
"37": {
"name": "yourAddress37",
"order": "6",
"sublabels": "{\"cc_firstName\":\"First Name\",\"cc_lastName\":\"Last Name\",\"cc_number\":\"Credit Card Number\",\"cc_ccv\":\"Security Code\",\"cc_exp_month\":\"Expiration Month\",\"cc_exp_year\":\"Expiration Year\",\"addr_line1\":\"Street Address\",\"addr_line2\":\"Street Address Line 2\",\"city\":\"City\",\"state\":\"State \\/ Province\",\"postal\":\"Postal \\/ Zip Code\",\"country\":\"Country\"}",
"text": "Your Home Address:",
"type": "control_address",
"answer": {
"addr_line1": "148 east 38st ",
"addr_line2": "",
"city": "Brooklyn ",
"state": "Ny",
"postal": "11203",
"country": ""
},
"prettyFormat": "Street Address: 148 east 38st <br>City: Brooklyn <br>State / Province: Ny<br>Postal / Zip Code: 11203<br>"
},
"38": {
"name": "emergencyContact",
"order": "9",
"sublabels": "{\"prefix\":\"Prefix\",\"first\":\"First Name\",\"middle\":\"Middle Name\",\"last\":\"Last Name\",\"suffix\":\"Suffix\"}",
"text": "Emergency Contact Name:",
"type": "control_fullname",
"answer": {
"first": "Pauline ",
"last": "Sandy "
},
"prettyFormat": "Pauline Sandy "
}
}
and it MAPS to following c# property
public Dictionary<int, answer> answers{ get; set; }
Then I have a generic Answer class
public class answer
{
public string name { get; set; }
public dynamic answer { get; set; }
}
if you look at the ANSWER data from json then you will see its different for every question. for example one answer would be ADDRESS OBJECT, other answer would be FIRST & LAST NAME object.
my question is, how can i deserialize json into correct objects/properties automatically? I can create different POCO objects, such as address & ProfileName, but how would i map them automatically to correct object/property.
EDIT:
Loop through all Answers
foreach (var a in item.answers)
{
// pass the ANSWER OBJECT (dynamic data type) to function
createNewApplication(System.Convert.ToInt16(a.Key), a.Value.answer,ref app);
}
private void createNewApplication(int key, dynamic value,ref HcsApplicant app)
{
if (key == 4) // data is plain string
app.yourPhone = value;
if (key == 8)
app.yourEmail = value;
if (key==37) // data is a object
app.address = value.ToObject<address>();
}
is this approach OK? any cleaner way of doing it?
I personally don't like every option that involves custom parsing and looking directly on the questions.
You can make use of partial deserialization via JToken class.
Just declare your answers dictionary as such:
public Dictionary<int, JToken> Answers{ get; set; }
And then whenever you need the address page you can simply do Answers[37].ToObject<Address>(). How you manage to call this method, depends upon the rest of your code, but you can embed it in properties, in a big switch, in multiple methods, one for each class. One option I like is to have a static From method in each deserializable class:
public class Address
{
public string Name { get; set; }
// all the othe properties
// ....
public static Address From(Dictionary<int, JToken> answers)
{
return answers?.TryGetValue(37, out var address) ?? false
? address?.ToObject<Address>()
: null;
}
}
// so you can just write:
var address = Address.From(answers);
As a side note, remember that the default deserialization settings for Json.Net are case insensitive, so you can deserialize the name property from JSON to a more idiomatic Name property on your POCOs.
Make a constructor for each answer type that constructs by parsing a JSON object string. Make all the answers implement an interface, e.g. IAnswer. Map all constructors (as functions) to the corresponding question IDs in a dictionary. Lastly, loop through the questions, call each constructor, and maybe put them in a new dictionary.
Example code:
interface IAnswer { };
public class ExampleAnswer : IAnswer
{
public ExampleAnswer(String JSONObject)
{
// Parse JSON here
}
}
delegate IAnswer AnswerConstructor(String JSONObject);
Dictionary<int, AnswerConstructor> Constructors = new Dictionary<int, AnswerConstructor>()
{
{1234, ((AnswerConstructor)(json => new ExampleAnswer(json)))}
// Add all answer types here
};
Dictionary<int, IAnswer> ParseAnswers(Dictionary<int, String> JSONObjects)
{
var result = new Dictionary<int, IAnswer>();
foreach (var pair in JSONObjects)
result.Add(pair.Key, Constructors[pair.Key](pair.Value));
return result;
}
Edit: Look at Matt's answer for some good options for how to parse JSON.
Edit2, In response to your edit: That looks like a good way of doing it! I think it's better than my answer, since you can keep all type information, unlike my method.
The only thing I see that you might want to change is using else if or switch instead of multiple ifs. This could increase performance if you have many answers.
You have a couple of options:
Deserialize into a dynamic object using the System.Web package as per this answer or the JSON.Net package as per this answer then use conditional checks/the null propagation operator to access a property.
Automatically deserialize down to the level where there are differences, then have code to manual deserialize the properties that are different into the correct POCO types on your parent Deserialized object.
Leverage one of the Serialization Callbacks provided by JSON.Net (OnDeserializing or OnDeserialized) to handle populating the different properties into the correct types as part of the deserialization pipeline.
With approaches 2 and 3 you could write a nicer helper method on your POCO that inspected the objects properties and returned a result which would be the type that was set (I would recommend returning an Enum) e.g.:
public PropertyTypeEnum GetPropertyType(MyPocoClass myPocoClass)
{
if (myPocoClass.PropertyOne != null)
{
return PropertyTypeEnum.TypeOne;
}
else if (...)
{
return PropertyTypeEnum.TypeN
}
else
{
// probably throw a NotImplementedException here depending on your requirements
}
}
Then in your code to use the object you can use the returned Enum to switch on the logical paths of your code.

Serializing C# classes to MongoDB without using discriminators in subdocuments

I'm writing C# code that writes to a Mongo database used by an existing Web app (written in PHP), so I need to not change the existing structure of the database. The database structure looks something like this:
{
"_id": ObjectId("5572ee670e86b8ec0ed82c61")
"name": "John Q. Example",
"guid": "12345678-1234-5678-abcd-fedcba654321",
"recordIsDeleted": false,
"address":
{
"line1": "123 Main St.",
"city": "Exampleville"
}
}
I read that in to a class that looks like this:
public class Person : MongoMappedBase
{
public ObjectId Id { get; set; }
public Guid Guid { get; set; }
public bool RecordIsDeleted { get; set; }
public string Name { get; set; }
public AddressData Address { get; set; }
// etc.
}
public class AddressData : MongoMappedBase
{
public string Line1 { get; set; }
public string City { get; set; }
// etc.
}
The reading code looks like:
var collection = db.GetCollection<Person>("people");
List<Person> people = collection.Find<Person>(_ => true).ToListAsync().Result;
(Note: I'm still in development. In production, I'm going to switch to ToCursorAsync() and loop through the data one at a time, so don't worry about the fact that I'm pulling the whole list into memory.)
So far, so good.
However, when I write the data out, this is what it looks like:
{
"_id": ObjectId("5572ee670e86b8ec0ed82c61")
"name": "John Q. Example",
"guid": "12345678-1234-5678-abcd-fedcba654321",
"recordIsDeleted": false,
"address":
{
"_t": "MyApp.MyNamespace.AddressData, MyApp",
"_v":
{
"line1": "123 Main St.",
"city": "Exampleville"
}
}
}
Notice how the address field looks different. That's not what I want. I want the address data to look just like the address data input (no _t or _v fields). In other words, the part that ended up as the contents of _v is what I wanted to persist to the Mongo database as the value of the address field.
Now, if I was just consuming the Mongo database from my own C# code, this would probably be fine: if I were to deserialize this data structure, I assume (though I haven't yet verified) that Mongo would use the _t and _v fields to create instances of the right type (AddressData), and put them in the Address property of my Person instances. In which case, everything would be fine.
But I'm sharing this database with a PHP web app that is not expecting to see those _t and _v values in the address data, and won't know what to do with them. I need to tell Mongo "Please do not serialize the type of the Address property. Just assume that it's always going to be an AddressData instance, and just serialize its contents without any discriminators."
The code I'm currently using to persist the objects to Mongo looks like this:
public UpdateDefinition<TDocument> BuildUpdate<TDocument>(TDocument doc) {
var builder = Builders<TDocument>.Update;
UpdateDefinition<TDocument> update = null;
foreach (PropertyInfo prop in typeof(TDocument).GetProperties())
{
if (prop.PropertyType == typeof(MongoDB.Bson.ObjectId))
continue; // Mongo doesn't allow changing Mongo IDs
if (prop.GetValue(doc) == null)
continue; // If we didn't set a value, don't change existing one
if (update == null)
update = builder.Set(prop.Name, prop.GetValue(doc));
else
update = update.Set(prop.Name, prop.GetValue(doc));
}
return update;
}
public void WritePerson(Person person) {
var update = BuildUpdate<Person>(person);
var filter = Builders<Person>.Filter.Eq(
"guid", person.Guid.ToString()
);
var collection = db.GetCollection<Person>("people");
var updateResult = collection.FindOneAndUpdateAsync(
filter, update
).Result;
}
Somewhere in there, I need to tell Mongo "I don't care about the _t field on the Address property, and I don't even want to see it. I know what type of objects I'm persisting into this field, and they'll always be the same." But I haven't yet found anything in the Mongo documentation to tell me how to do that. Any suggestions?
I figured it out. I was indeed having the problem described at https://groups.google.com/forum/#!topic/mongodb-user/QGctV4Hbipk where Mongo expects a base type but is given a derived type. The base type Mongo was expecting, given my code above, was actually object! I discovered that builder.Set() is actually a generic method, builder.Set<TField>, which can figure out its TField type parameter from the type of its second argument (the field data). Since I was using prop.GetValue(), which returns object, Mongo was expecting an object instance on my Address field (and the other fields that I left out of the question) and therefore putting _t on all those fields.
The answer was to explicitly cast the objects being returned from prop.GetValue(), so that builder.Set() could call the correct generic method (builder.Set<AddressData>() rather than builder.Set<object>()) in this case. The following was a bit ugly (I wish there was a way to get a specific generic function overload by reflection at runtime, as I could have converted that whole switch statement to a single reflection-based method call), but it worked:
public UpdateDefinition<TDocument> BuildUpdate<TDocument>(TDocument doc) {
var builder = Builders<TDocument>.Update;
var updates = new List<UpdateDefinition<TDocument>>();
foreach (PropertyInfo prop in typeof(TDocument).GetProperties())
{
if (prop.PropertyType == typeof(MongoDB.Bson.ObjectId))
continue; // Mongo doesn't allow changing Mongo IDs
if (prop.GetValue(doc) == null)
continue; // If we didn't set a value, don't change existing one
switch (prop.PropertyType.Name) {
case "AddressData":
updates.add(builder.Set(prop.Name, (AddressData)prop.GetValue(doc)));
break;
// Etc., etc. Many other type names here
default:
updates.add(builder.Set(prop.Name, prop.GetValue(doc)));
break;
}
}
return builder.Combine(updates);
}
This resulted in the Address field, and all the other fields I was having trouble with in my real code, being persisted without any _t or _v fields, just like I wanted.
Thanks #rmunn for this question, it helped me a lot.
I was struggling with this same problem when I found this Q&A. After further digging I found that you can remove the switch statement in the accepted answer by using BsonDocumentWrapper.Create(). This is a link to where I found the tip.
Here's a example for anyone else looking:
public UpdateDefinition<TDocument> BuildUpdate<TDocument>(TDocument doc) {
var builder = Builders<TDocument>.Update;
var updates = new List<UpdateDefinition<TDocument>>();
foreach (PropertyInfo prop in typeof(TDocument).GetProperties())
{
if (prop.PropertyType == typeof(MongoDB.Bson.ObjectId))
continue; // Mongo doesn't allow changing Mongo IDs
if (prop.GetValue(doc) == null)
continue; // If we didn't set a value, don't change existing one
updates.add(builder.Set(prop.Name, BsonDocumentWrapper.Create(prop.PropertyType, prop.GetValue(doc))));
}
return builder.Combine(updates);
}
You can convert your object to JSON string and from that JSON string you can convert back to BsonArray (if list) or BsonDocument (if object)
Object that you want to update
public UpdateDefinition<T> getUpdate(T t)
{
PropertyInfo[] props = typeof(T).GetProperties();
UpdateDefinition<T> update = null;
foreach (PropertyInfo prop in props)
{
if (t.GetType().GetProperty(prop.Name).PropertyType.Name == "List`1")
{
update = Builders<T>.Update.Set(prop.Name, BsonSerializer.Deserialize<BsonArray>(JsonConvert.SerializeObject(t.GetType().GetProperty(prop.Name).GetValue(t))));
}
else if (t.GetType().GetProperty(prop.Name).PropertyType.Name == "object")
{
/* if its object */
update = Builders<T>.Update.Set(prop.Name, BsonSerializer.Deserialize<BsonDocument>(JsonConvert.SerializeObject(t.GetType().GetProperty(prop.Name).GetValue(t))));
}
else
{
/*if its primitive data type */
update = Builders<T>.Update.Set(prop.Name, t.GetType().GetProperty(prop.Name).GetValue(t));
}
}
return update;
}
This will update any type of object list, you just need to pass the object

Architecturally speaking, how should I replace an extremely large switch statement with something more manageable?

EDIT 1: Forgot to add the nested property curve ball.
UPDATE: I have chosen #mtazva's answer as that was the preferred solution for my specific case. In retrospect, I asked a general question with a very specific example and I believe that ended up confusing everyone (or maybe just me) as to what the question was exactly. I do believe the general question has been answered as well (see the Strategy pattern answers and links). Thanks everyone!
Large switch statements obviously smell and I have seen some links on how you could do this with a dictionary that maps to functions. But I'm wondering if there is a better (or smarter way) to do this? In a way, this is a question I've always sort of had rolling around in the back of my head but never really had a good solution to.
This question stemmed from another question I asked earlier: How to select all the values of an object's property on a list of typed objects in .Net with C#
Here is an example class I'm working with (from an external source):
public class NestedGameInfoObject
{
public string NestedName { get; set; }
public int NestedIntValue { get; set; }
public decimal NestedDecimalValue { get; set; }
}
public class GameInfo
{
public int UserId { get; set; }
public int MatchesWon { get; set; }
public long BulletsFired { get; set; }
public string LastLevelVisited { get; set; }
public NestedGameInfoObject SuperCoolNestedGameInfo { get; set; }
// thousands more of these
}
Unfortunately, this is coming from an external source... imagine a HUGE data dump from Grand Theft Auto or something.
And I want to get just a small cross section of a list of these objects. Imagine we want to be able to compare you with a bunch of your friends' game info objects. An individual result for one user would look like this:
public class MyResult
{
public int UserId { get; set; } // user id from above object
public string ResultValue { get; set; } // one of the value fields from above with .ToString() executed on it
}
And an example of what I want to replace with something more manageable (believe me, I DON'T want to be maintaining this monster switch statement):
const int MATCHES_WON = 1;
const int BULLETS_FIRED = 2;
const int NESTED_INT = 3;
public static List<MyResult> GetMyResult(GameInfo[] gameInfos, int input)
{
var output = new List<MyResult>();
switch(input)
{
case MATCHES_WON:
output = gameInfos.Select(x => new MyResult()
{
UserId = x.UserId,
ResultValue = x.MatchesWon.ToString()
}).ToList<MyResult>();
break;
case BULLETS_FIRED:
output = gameInfos.Select(x => new MyResult()
{
UserId = x.UserId,
ResultValue = x.BulletsFired.ToString()
}).ToList<MyResult>();
break;
case NESTED_INT:
output = gameInfos.Select(x => new MyResult()
{
UserId = x.UserId,
ResultValue = x.SuperCoolNestedGameInfo.NestedIntValue.ToString()
}).ToList<MyResult>();
break;
// ad nauseum
}
return output;
}
So the question is are there any reasonable ways to manage this beast? What I'd really like is a dynamic way to get this info in case that initial object changes (more game info properties are added, for instance). Is there a better way to architect this so it's less clumsy?
I think your first sentence eluded to what is probably the most reasonable solution: some form of dictionary mapping values to methods.
For example, you could define a static Dictionary<int, func<GameInfo, string>>, where each value such as MATCHES_WON would be added with a corresponding lambda that extracts the appropriate value (assuming your constants, etc are defined as shown in your example):
private static Dictionary<int, Func<GameInfo, string>> valueExtractors =
new Dictionary<int, Func<GameInfo, string>>() {
{MATCHES_WON, gi => gi.MatchesWon.ToString()},
{BULLETS_FIRED, gi => gi.BulletsFired.ToString()},
//.... etc for all value extractions
};
You can then use this dictionary to extract the value in your sample method:
public static List<MyResult> GetMyResult(GameInfo[] gameInfos, int input)
{
return gameInfo.Select(gi => new MyResult()
{
UserId = gi.UserId,
ResultValue = valueExtractors[input](gi)
}).ToList<MyResult>();
}
Outside of this option, you could potentially have some sort of file/database/stored lookup with the number and the property name, then use reflection to extract the value, but that would obviously not perform as well.
I think this code is getting out of hand a bit. You're effectively using constants to index properties - and this is creating fragile code that you're looking to use some technique - such as - reflection, dictionaries, etc - to control the increased complexity.
Effectively the approach that you're using now will end up with code like this:
var results = GetMyResult(gameInfos, BULLETS_FIRED);
The alternative is to define an extension method that lets you do this:
var results = gameInfos.ToMyResults(gi => gi.BulletsFired);
This is strongly-typed, it doesn't require constants, switch statements, reflection, or anything arcane.
Just write these extension methods and you're done:
public static class GameInfoEx
{
public static IEnumerable<MyResult> ToMyResults(
this IEnumerable<GameInfo> gameInfos,
Func<GameInfo, object> selector)
{
return gameInfos.Select(gi => gi.ToMyResult(selector));
}
public static MyResult ToMyResult(
this GameInfo gameInfo,
Func<GameInfo, object> selector)
{
return new MyResult()
{
UserId = gameInfo.UserId,
ResultValue = selector(gameInfo).ToString()
};
}
}
Does that work for you?
You can use reflection for theses purposes. You can implement custom attributes, mark your properties, etc. Also, it is dynamic way to get info about your class if it changes.
If you want to manage switch code I would point you at Design Patterns book (GoF) and suggest possibly looking at patterns like Strategy and possibly Factory (thats when we talk about general case use, your case isn't very suited for Factory) and implementing them.
While switch statement still has to be left somewhere after refactoring to pattern is complete (for example, in a place where you select strategy by id), code will be much more maintanable and clear.
That said about general switch maintenance, if they become beast like, I am not sure its best solution given how similar your case statements look.
I am 100% sure you can create some method (possibly an extension method) that will be accepting desired property accessor lambda, that should be used when results are generated.
If you want your code to be more generic, I agree with the suggestion of a dictionary or some kind of lookup pattern.
You could store functions in the dictionary, but they seemly all perform the same operation - getting the value from a property. This is ripe for reflection.
I'd store all your properties in a dictionary with an enum (prefer an enum to a const) as the key, and a PropertyInfo - or, less preferred, a string which describes the name of the property - as the value. You then call the GetValue() method on the PropertyInfo object to retrieve the value from the object / class.
Here's an example where I'm mapping enum values to their 'same named' properties in a class, and then using reflection to retrieve the values out of a class.
public enum Properties
{
A,
B
}
public class Test
{
public string A { get; set; }
public int B { get; set; }
}
static void Main()
{
var test = new Test() { A = "A value", B = 100 };
var lookup = new Dictionary<Properties, System.Reflection.PropertyInfo>();
var properties = typeof(Test).GetProperties().ToList();
foreach (var property in properties)
{
Properties propertyKey;
if (Enum.TryParse(property.Name, out propertyKey))
{
lookup.Add(propertyKey, property);
}
}
Console.WriteLine("A is " + lookup[Properties.A].GetValue(test, null));
Console.WriteLine("B is " + lookup[Properties.B].GetValue(test, null));
}
You can map your const values to the names of the properties, PropertyInfo objects which relate to those properties, functions which will retrieve the property values... whatever you think suits your needs.
Of course you will need some mapping - somewhere along the way you will be depending on your input value (the const) mapping to a specific property. The method by which you can get this data might determine the best mapping structure and pattern for you.
I think the way to go is indeed some kind of mapping from one value (int) to something that is somehow a function that knows how to extract a value.
If you really want to keep it extensible, so that you can easily add some without touching the code, and possibly accessing more complex properties (ie. nested properties, do some basic computation), you may want to keep that in a separate source.
I think one way to do this is to rely on the Scripting Services, for instance evaluating a simple IronPython expression to extract a value...
For instance in a file you could store something like :
<GameStats>
<GameStat name="MatchesWon" id="1">
<Expression>
currentGameInfo.BulletsFired.ToString()
</Expression>
</GameStat>
<GameStat name="FancyStat" id="2">
<Expression>
currentGameInfo.SuperCoolNestedGameInfo.NestedIntValue.ToString()
</Expression>
</GameStat>
</GameStats>
and then, depending on the requested stat, you always end up retrieving the general GameInfos. You can them have some kind of foreach loop with :
foreach( var gameInfo in gameInfos){
var currentGameInfo = gameInfo
//evaluate the expression for this currentGameInfo
return yield resultOfEvaluation
}
See http://www.voidspace.org.uk/ironpython/dlr_hosting.shtml for examples on how to embed IronPython Scripting in a .NET application.
NOTE: when working with this kind of stuff, there are several things you must really be careful about:
this potentially allows someone to inject code in your application ...
you should measure the performance impact of Dynamic evaluation in here
I don't have a solution to your switch problem off the top of my head, but you could certainly reduce the code by using a class that can automatically map all the fields you need. Check out http://automapper.org/.
I would not have written the GetMyResult method in the first place. All it is doing is transforming GameInfo sequence into MyResult sequence. Doing it with Linq would be easier and more expressive.
Instead of calling
var myResultSequence = GetMyResult(gameInfo, MatchesWon);
I would simply call
var myResultSequence = gameInfo.Select(x => new MyResult() {
UserId = x.UserId,
ResultValue = x.MatchesWon.ToString()
});
To make it more succinct you can pass the UserId and ResultValue in constructor
var myResultSequence =
gameInfo.Select(x => new MyResult(x.UserId, x.MatchesWon.ToString()));
Refactor only if you see the selects getting duplicated too much.
This is one possible way without using reflection:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public class GameInfo
{
public int UserId { get; set; }
public int MatchesWon { get; set; }
public long BulletsFired { get; set; }
public string LastLevelVisited { get; set; }
// thousands more of these
}
public class MyResult
{
public int UserId { get; set; } // user id from above object
public string ResultValue { get; set; } // one of the value fields from above with .ToString() executed on it
}
public enum DataType
{
MatchesWon = 1,
BulletsFired = 2,
// add more as needed
}
class Program
{
private static Dictionary<DataType, Func<GameInfo, object>> getDataFuncs
= new Dictionary<DataType, Func<GameInfo, object>>
{
{ DataType.MatchesWon, info => info.MatchesWon },
{ DataType.BulletsFired, info => info.BulletsFired },
// add more as needed
};
public static IEnumerable<MyResult> GetMyResult(GameInfo[] gameInfos, DataType input)
{
var getDataFunc = getDataFuncs[input];
return gameInfos.Select(info => new MyResult()
{
UserId = info.UserId,
ResultValue = getDataFunc(info).ToString()
});
}
static void Main(string[] args)
{
var testData = new GameInfo[] {
new GameInfo { UserId="a", BulletsFired = 99, MatchesWon = 2 },
new GameInfo { UserId="b", BulletsFired = 0, MatchesWon = 0 },
};
// you can now easily select whatever data you need, in a type-safe manner
var dataToGet = DataType.MatchesWon;
var results = GetMyResult(testData, dataToGet);
}
}
}
Purely on the question of large switch statements, it is notable that there are 2 variants of the Cyclomatic Complexity metric in common use. The "original" counts each case statement as a branch and so it increments the complexity metric by 1 - which results in a very high value caused by many switches. The "variant" counts the switch statement as a single branch - this is effectively considering it as a sequence of non-branching statements, which is more in keeping with the "understandability" goal of controlling complexity.

How can I refactor this C# code

Basically I have a method that takes an object and sets another objects properties based on the object passed in.
e.g:
private void SetObject(MyClass object)
{
MyClass2 object2 = new MyClass2();
object2.Property1 = HelperClass.Convert(object.Property1);
//....
// Lots more code ....
//....
}
Now the method is 53 lines long because there are alot of properties to set. The method seems too long to me but I'm struggling to work out how I can possibly break it down.
One option is to try and group up similar properties and pass the object around as a reference to different methods that set these similar properties, but that doesn't seem to sit right with me.
Or I could create a constructor for MyClass2 that accepts a MyClass1 but that doesn't seem right either.
Anyway would welcome some suggestions.
EDIT: Ok thanks for the replies I'll have to give more info, the property names arent the same and I have to call some conversion methods as well. Reflection wouldn't be good because of this and also the performance hit. Automapper I think for the same reasons.
A real example of the code:
private ReportType GetReportFromItem(SPWeb web, SPListItem item)
{
ReportType reportType = new ReportType();
reportType.ReportID = int.Parse(item["Report ID"].ToString());
reportType.Name = item["Title"].ToString();
reportType.SourceLocation = FieldHelpers.GetUri(item["Source Location"]);
reportType.TargetLocation = FieldHelpers.GetUri(item["Document Library"]);
SPFieldUserValue group1 =
new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 1"));
reportType.SecurityGroup1 = group1.LookupValue;
SPFieldUserValue group2 =
new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 2"));
reportType.SecurityGroup2 = group2.LookupValue;
SPFieldUserValue group3 =
new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 3"));
reportType.SecurityGroup3 = group3.LookupValue;
SPFieldUserValue group4 =
new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 4"));
// More code
//...
//...
}
Sounds like a job for AutoMapper
use reflection to do it. probably have a method like this:
private void SetProperties<T>(List<T> objects, List<Tuple<string, object>> propsAndValues) where T:<your_class>
{
Type type = typeof(T);
var propInfos = propsAndValues.ToDictionary(key => type.GetProperty(key.Item1, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.SetProperty), elem => elem.Item2);
objects.AsParallel().ForAll(obj =>
{
obj.SetProps(propInfos);
});
}
public static void SetProps<T>(this T obj, Dictionary<PropertyInfo, object> propInfos) where T : <your_class>
{
foreach (var propInfo in propInfos)
{
propInfo.Key.SetValue(obj, propInfo.Value, null);
}
}
There are a few strategies that came to my mind for doing this, all with their own advantages and disadvantages. Also, I was not familiar with it, but the AutoMapper tool linked to in a different answer to your question sounds like it also could be a good solution. (Also, if there were any way of deriving your classes all from the same class, or storing the properties themselves in a struct instead of directly in the classes, those seem like things to consider as well.)
Reflection
This was mentioned in this answer. However, I am not sure I totally understand the intended use of the functions in that answer, since I don't see a GetValue call, nor any mapping between two types. Also, I can see times where you might want to create something to allow for two different names to map to one another, or for conversion between two types. For a rather generic solution, I would probably go with an approach like this:
Create an extension class with one or more methods that will use reflection to copy based on identical property names and/or pre-defined configuration objects.
For each pair of types that has properties that won't have identical names, create a configuration object mapping the names to each other.
For properties that you won't want to be copied, create a configuration object that holds a list of names to ignore.
I don't actually see anything objectionable about passing one class to another in the constructor if the intent is to copy the properties, seems more of a matter of style than anything hard and fast.
Example Code
classes to be copied to:
public class MyClass2
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public string Property3WithOtherName { get; set; }
public double Property4 { get; set; }
public string Property5WithDifferentName { get; set; }
public string TestIntToString { get; set; }
public int TestStringToInt { get; set; }
}
public class MyClass3
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public string Prop3OtherName { get; set; }
public double Prop4 { get; set; }
public string Prop5DiffName { get; set; }
public string PropOnlyClass3 { get; set; }
public string[] StringArray { get; set; }
}
class to be copied, w/ mapping info to the other objects:
public class MyClass
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public string Property3 { get; set; }
public double Property4 { get; set; }
public string Property5 { get; set; }
public double PropertyDontCopy { get; set; }
public string PropertyOnlyClass3 { get; set; }
public int[] PropertyIgnoreMe { get; set; }
public string[] StringArray { get; set; }
public int TestIntToString { get; set; }
public string TestStringToInt { get; set; }
# region Static Property Mapping Information
// this is one possibility for creating and storing the mapping
// information: the class uses two dictionaries, one that links
// the other type with a dictionary of mapped properties, and
// one that links the other type with a list of excluded ones.
public static Dictionary<Type, Dictionary<string, string>>
PropertyMappings =
new Dictionary<Type, Dictionary<string, string>>
{
{
typeof(MyClass2),
new Dictionary<string, string>
{
{ "Property3", "Property3WithOtherName" },
{ "Property5", "Property5WithDifferentName" },
}
},
{
typeof(MyClass3),
new Dictionary<string, string>
{
{ "Property1", "Prop1" },
{ "Property2", "Prop2" },
{ "Property3", "Prop3OtherName" },
{ "Property4", "Prop4" },
{ "Property5", "Prop5DiffName" },
{ "PropertyOnlyClass3", "PropOnlyClass3" },
}
},
};
public static Dictionary<Type, List<string>>
UnmappedProperties =
new Dictionary<Type, List<string>>
{
{
typeof(MyClass2),
new List<string>
{
"PropertyDontCopy",
"PropertyOnlyClass3",
"PropertyIgnoreMe"
}
},
{
typeof(MyClass3),
new List<string>
{
"PropertyDontCopy",
"PropertyIgnoreMe"
}
}
};
// this function pulls together an individual property mapping
public static Tuple<Dictionary<string, string>, List<string>>
MapInfo<TOtherType>()
{
return
new Tuple<Dictionary<string,string>,List<string>>
(
PropertyMappings[typeof(TOtherType)],
UnmappedProperties[typeof(TOtherType)]
);
}
#endregion
}
Mapping Extension Class:
public static class MappingExtensions
{
// this is one possibility for setting up object mappings
#region Type Map Definition Section
// * set up the MapInfo<TOther>() call in each object to map
// * setup as follows to map two types to an actual map
public static Tuple<Type, Type> MapFromTo<TFromType, TToType>()
{
return Tuple.Create<Type,Type>(typeof(TFromType), typeof(TToType));
}
static Dictionary<
Tuple<Type, Type>,
Tuple<Dictionary<string, string>, List<string>>
>
MappingDefinitions =
new Dictionary <
Tuple<Type,Type>,
Tuple<Dictionary<string,string>,List<string>>
>
{
{ MapFromTo<MyClass,MyClass2>(), MyClass.MapInfo<MyClass2>() },
{ MapFromTo<MyClass,MyClass3>(), MyClass.MapInfo<MyClass3>() },
};
#endregion
// method using Reflection.GetPropertyInfo and mapping info to do copying
// * for fields you will need to reflect using GetFieldInfo() instead
// * for both you will need to reflect using GetMemberInfo() instead
public static void CopyFrom<TFromType, TToType>(
this TToType parThis,
TFromType parObjectToCopy
)
{
var Map = MappingDefinitions[MapFromTo<TFromType, TToType>()];
Dictionary<string,string> MappedNames = Map.Item1;
List<string> ExcludedNames = Map.Item2;
Type FromType = typeof(TFromType); Type ToType = typeof(TToType);
// ------------------------------------------------------------------------
// Step 1: Collect PIs for TToType and TFromType for Copying
// ------------------------------------------------------------------------
// Get PropertyInfos for TToType
// the desired property types to reflect for ToType
var ToBindings =
BindingFlags.Public | BindingFlags.NonPublic // property visibility
| BindingFlags.Instance // instance properties
| BindingFlags.SetProperty; // sets for ToType
// reflect an array of all properties for this type
var ToPIs = ToType.GetProperties(ToBindings);
// checks for mapped properties or exclusions not defined for the class
#if DEBUG
var MapErrors =
from name in
MappedNames.Values
where !ToPIs.Any(pi => pi.Name == name)
select string.Format(
"CopyFrom<{0},{1}>: mapped property '{2}' not defined for {1}",
FromType.Name, ToType.Name, name
);
#endif
// ------------------------------------------------------------------------
// Get PropertyInfos for TFromType
// the desired property types to reflect; if you want to use fields, too,
// you can do GetMemberInfo instead of GetPropertyInfo below
var FromBindings =
BindingFlags.Public | BindingFlags.NonPublic // property visibility
| BindingFlags.Instance // instance/static
| BindingFlags.GetProperty; // gets for FromType
// reflect all properties from the FromType
var FromPIs = FromType.GetProperties(FromBindings);
// checks for mapped properties or exclusions not defined for the class
#if DEBUG
MapErrors = MapErrors.Concat(
from mn in MappedNames.Keys.Concat(
ExcludedNames)
where !FromPIs.Any(pi => pi.Name == mn)
select string.Format(
"CopyFrom<{0},{1}>: mapped property '{2}' not defined for {1}",
FromType.Name, ToType.Name, mn
)
);
// if there were any errors, aggregate and throw
if (MapErrors.Count() > 0)
throw new Exception(
MapErrors.Aggregate(
"", (a,b)=>string.Format("{0}{1}{2}",a,Environment.NewLine,b)
));
#endif
// exclude anything in the exclusions or not in the ToPIs
FromPIs = FromPIs.Where(
fromPI =>
!ExcludedNames.Contains(fromPI.Name)
&& ToPIs.Select(toPI => toPI.Name).Concat(MappedNames.Keys)
.Contains(fromPI.Name)
)
.ToArray();
// Step 1 Complete
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Step 2: Copy Property Values from Source to Destination
#if DEBUG
Console.WriteLine("Copying " + FromType.Name + " to " + ToType.Name);
#endif
// we're using FromPIs to drive the loop because we've already elimiated
// all items that don't have a matching value in ToPIs
foreach (PropertyInfo FromPI in FromPIs)
{
PropertyInfo ToPI;
// if the 'from' property name exists in the mapping, use the mapped
// name to find ToPI, otherwise use ToPI matching the 'from' name
if (MappedNames.Keys.Contains(FromPI.Name))
ToPI = ToPIs.First(pi => pi.Name == MappedNames[FromPI.Name]);
else
ToPI = ToPIs.First(pi => pi.Name == FromPI.Name);
Type FromPropertyType = FromPI.PropertyType;
Type ToPropertyType = ToPI.PropertyType;
// retrieve the property value from the object we're copying from; keep
// in mind if this copies by-reference for arrays and other ref types,
// so you will need to deal with it if you want other behavior
object PropertyValue = FromPI.GetValue(parObjectToCopy, null);
// only need this if there are properties with incompatible types
// * implement IConvertible for user-defined types to allow conversion
// * you can try/catch if you want to ignore items which don't convert
if (!ToPropertyType.IsAssignableFrom(FromPropertyType))
PropertyValue = Convert.ChangeType(PropertyValue, ToPropertyType);
// set the property value on the object we're copying to
ToPI.SetValue(parThis, PropertyValue, null);
#if DEBUG
Console.WriteLine(
"\t"
+ "(" + ToPI.PropertyType.Name + ")" + ToPI.Name
+ " := "
+ "(" + FromPI.PropertyType.Name + ")" + FromPI.Name
+ " == "
+ ((ToPI.PropertyType.Name == "String") ? "'" : "")
+ PropertyValue.ToString()
+ ((ToPI.PropertyType.Name == "String") ? "'" : "")
);
#endif
}
// Step 2 Complete
// ------------------------------------------------------------------------
}
}
Test Method:
public void RunTest()
{
MyClass Test1 = new MyClass();
Test1.Property1 = 1;
Test1.Property2 = 2;
Test1.Property3 = "Property3String";
Test1.Property4 = 4.0;
Test1.Property5 = "Property5String";
Test1.PropertyDontCopy = 100.0;
Test1.PropertyIgnoreMe = new int[] { 0, 1, 2, 3 };
Test1.PropertyOnlyClass3 = "Class3OnlyString";
Test1.StringArray = new string[] { "String0", "String1", "String2" };
Test1.TestIntToString = 123456;
Test1.TestStringToInt = "654321";
Console.WriteLine("-------------------------------------");
Console.WriteLine("Copying: Test1 to Test2");
Console.WriteLine("-------------------------------------");
MyClass2 Test2 = new MyClass2();
Test2.CopyFrom(Test1);
Console.WriteLine("-------------------------------------");
Console.WriteLine("Copying: Test1 to Test3");
Console.WriteLine("-------------------------------------");
MyClass3 Test3 = new MyClass3();
Test3.CopyFrom(Test1);
Console.WriteLine("-------------------------------------");
Console.WriteLine("Done");
Console.WriteLine("-------------------------------------");
}
}
Output:
-------------------------------------
Copying: Test1 to Test2
-------------------------------------
Copying MyClass to MyClass2
(Int32)Property1 := (Int32)Property1 == 1
(Int32)Property2 := (Int32)Property2 == 2
(String)Property3WithOtherName := (String)Property3 == 'Property3String'
(Double)Property4 := (Double)Property4 == 4
(String)Property5WithDifferentName := (String)Property5 == 'Property5String'
(String)TestIntToString := (Int32)TestIntToString == '123456'
(Int32)TestStringToInt := (String)TestStringToInt == 654321
-------------------------------------
Copying: Test1 to Test3
-------------------------------------
Copying MyClass to MyClass3
(Int32)Prop1 := (Int32)Property1 == 1
(Int32)Prop2 := (Int32)Property2 == 2
(String)Prop3OtherName := (String)Property3 == 'Property3String'
(Double)Prop4 := (Double)Property4 == 4
(String)Prop5DiffName := (String)Property5 == 'Property5String'
(String)PropOnlyClass3 := (String)PropertyOnlyClass3 == 'Class3OnlyString'
(String[])StringArray := (String[])StringArray == System.String[]
-------------------------------------
Done
-------------------------------------
NOTE: if you have use for copying back the other direction, or would like, for instance, to create your mappings with a code generator, you may want to go with having your mappings in a separate static variable somewhere else, mapped by both types, rather than storing your mappings directly within a class. If you want to reverse direction, you could probably just add a flag to the existing code that lets you invert the meanings of the tables and of the passed types, although I would recommend changing references to TToType and TFromType to TType1 and TType2.
For having a code generator generate the mapping it's probably a good bit easier to separate it into the separate class with two type parameters for the generic, so that you don't necessarily have to worry about putting those definitions directly within your classes; I was torn about how to do it when I wrote the code, but I do think that would probably have been a more flexible option. (On the other hand, it probably means needing larger overall structure, which is why I broke it out like I did in the first place.) Another advantage to this way is that you don't necessarily need to create everything all at once, you could conceivable change to member variables that let you create them on-the-fly as needed, possibly through a function param or through an additional interface on your object.
Code Generation
This can be used by itself, it can also be used as a tool in conjunction with either of the next two methods. But the idea is that you can create a CSV or use some other method to hold your mapping data, and then you can create class templates (in separate files or in code) with placeholders in them (e.g., ${PROPERTIES_LIST}) that you can use to do .Replace() operations on, or you can create templates of the .xsd files used for typed dataset generation in the next section, generate the individual lines of the .xsd from the list, or, finally you could create the structures that hold the mapping information for something like the solution I give in the reflection section.
One thing I've done in the past which was very handy was to just whip up a typed dataset with a table structure that can hold all the information I need to do my code generation, and use the old .NET 2.0 version of the GridView to allow me to enter the data in. But even with just a simple XmlDocument or CSV file(s), it should be very easy to enter your properties mappings and read them back in, and the none of the generation for any of the scenarios here is likely to be terribly much effort, usually even compared to having to type it all in by hand just once or twice, plus if it's something that gets updated at all often, you will eventually save yourself hand-coding errors and debug time as well.
Typed Data Sets
Although it's sort of beyond the intended use of the data set, if you don't need super-high performance, it can come in handy. After defining a table in the dataset and building, you have a set of strongly-typed objects that can be used, for instance, to hold objects representing the tables and columns or rows. So, you could easily create a typed dataset from scratch using column names and types matching your objects, and then loop through the column names to get the names of your properties. While not as powerful as reflection, it could be a very quick way of getting yourself set up to copy two objects with the same property names. Of course, it's a much more limited usefulness because it's only going to be useful if you have control over your types, and if you don't have any particularly complicated needs for copying the data.
But it also can let you do things like:
MyClassDataRow Object1 = MyDataSet.MyClassTable.NewRow();
Object1.Prop1 = 123;
Object2.Prop2 = "Hello Dataset";
// etc...
MyClass2DataRow Object2 = MyDataSet.MyClass2Table.NewRow();
foreach (DataColumn ColumnName in MyClassTable.Columns)
Object2[ColumnName] = Object1[ColumnName];
with little additional effort. And, if you're in a situation where you want to be able save your objects out to disk, just add the rows to the table you've got a great way of serializing the data with the DataSet.ReadXml and DataSet.WriteXml calls.
Also, for the reflection solution, as long as your requirements weren't too complicated, you could create your mapping definitions by creating a typed dataset with a couple of tables with column names matching those to map, and then using the information in the DataColumns of the tables to do your mapping definition instead of Dictionaries, Lists, and Tuples. Using some sort of predefined names prefixes or otherwise unused types in your table, you ought to be able to duplicate most of the functionality in the sample code.

Categories