Adding to reflected Dictionaries - c#

I have a legacy class that looks like this:
public class LegacyBusinessObject
{
....(100 similar fields in total)
public Dictionary<string, string> SomeBusinessValue1 = new Dictionary<string, string>();
public Dictionary<string, long> SomeBusinessValue2 = new Dictionary<string, long>();
public Dictionary<string, decimal> SomeBusinessValue3 = new Dictionary<string, decimal>();
....
}
whereas the string key denominates the provider this value came from.
So for context: "SomeBusinessValue1" could be a weight measurement, that differs depending on the lab that did it.
I want to merge several of these monsters into one object using reflection:
public LegacyBusinessObject Merge(Dictionary<string, LegacyBusinessObject> objects)
{
var result = new LegacyBusinessObject();
//Loop through all the business object's fields
foreach (var prop in typeof(LegacyBusinessObject).GetFields())
{
//Second loop through all the individual objects from different providers
foreach (var ep in objects)
{
//Here I would need to test for all possivle value types that could
//be in the dictionary: <string, string>, <string, long>...
//then cast to it and invoke the Add method like this:
var propDictionary = prop.GetValue(result) as Dictionary<string, string>;
propDictionary.Add(ep.Key, ep.Value);
}
}
return result;
}
Now this approach requires me to do a lot of clumsy casts for propDictionary. (I also tried consctructing the matching keyvaluepair<,> and an Activator to instance it; but i can't find a way to add this to another dictionary)
Can you think of a better way to perform this merge, that takes arbitrary dictionary value types?
Some more context:
I am getting a LegacyBusinessObject Obj1 with data from Lab A and Lab B that is stored in the dictionaries. No I am cleaning up the database and find out that another LegacyBusinessObject Obj2 has Data from Lab C and Lab D. As it turns out there was a mistake during ingestion and Obj1 and Obj2 are for the same product and have wrongfully been stored in two different LegacyBusinessObjects. I now want to merge the data to get a new LegacyBusinessObject with Data from Lab A through D

Quite unclear what you are exactly asking, but:
public static LegacyBusinessObject Merge(Dictionary<string, LegacyBusinessObject> objects)
{
var result = new LegacyBusinessObject();
foreach (var prop in typeof(LegacyBusinessObject).GetFields())
{
var propDictionaryNew = (IDictionary)prop.GetValue(result);
foreach (var dict in objects)
{
var propDictionaryOld = (IDictionary)prop.GetValue(dict.Value);
foreach (DictionaryEntry de in propDictionaryOld)
{
propDictionaryNew[de.Key] = de.Value;
// Or:
//((IDictionary)result).Add(de.Key, de.Value);
// But be aware of exceptions if de.Key is present in multiple dictionaries
}
}
}
return result;
}
and then, to test it:
var lbo1 = new LegacyBusinessObject
{
SomeBusinessValue1 = new Dictionary<string, string> { { "A1", "A2" }, { "B1", "B2" } },
SomeBusinessValue2 = new Dictionary<string, long> { { "C1", 1 }, { "D1", 2 } },
SomeBusinessValue3 = new Dictionary<string, decimal> { { "E1", 3 }, { "F1", 4 } },
};
var lbo2 = new LegacyBusinessObject
{
SomeBusinessValue1 = new Dictionary<string, string> { { "G1", "G2" }, { "H1", "H2" } },
SomeBusinessValue2 = new Dictionary<string, long> { { "I1", 5 }, { "J1", 6 } },
SomeBusinessValue3 = new Dictionary<string, decimal> { { "K1", 7 }, { "L1", 8 } },
};
var result = Merge(new Dictionary<string, LegacyBusinessObject> { { "X", lbo1 }, { "Y", lbo2 } });
I'm cheating a little here... Dictionary<,> implements the pre-generics interface IDictionary (that is different from IDictionary<,>) that uses object as key and value. In this way I don't have to support the different value types. When using reflection with generic collections, a good trick is to see if the non-generic interfaces are enough to do what you need (because they are much easier to handle with reflection).

Related

LINQ creating a new anonymous type when selecting a property that is a dictionary

I have a Simple POCO like this:
[JsonObject(NamingStrategyType = typeof (CamelCaseNamingStrategy))]
public class MyType
{
public string Id { get; set; }
public string Name;
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<string, object> TypeDetails { get; set; }
}
and a method that gives me a list of MyType
i'm trying to return a list of Dictionary<string,object> with all the TypeDetails of the object list i got.
like this:
types.Select((myType) => myType.TypeDetails)
however, when i run this, what i get is a list of Dictionaries, but with the following shape:
[
{TypeDetails: {the dictionary i want},
{TypeDetails: {the dictionary i want}},
(...)
]
what i was expecting:
[
{the dictionary i want},
{the dictionary i want},
(...)
]
to clarify: i'd like this to be true:
types[0].TypeDetails.equals(r[0])
It's the same as if i were doing
types.Select((myType) => new {myType.TypeDetails})
if i get any other property the select behaves as i expected it.
i can't find any doc that indicates whether dictionaries get a special treatment, nor any other clue about why this is happening.
Use .SelectMany() on the Dictionary's values, like so:
types.SelectMany((myType) => myType.TypeDetails.Values)
Either your return expectation/description is out:
i'm trying to return a list of Dictionary<string,Object>
or, (assuming (myType) is of type MyType), your real code does not match:
types.Select((myType) => myType.TypeDetails)
The example code below does return a collection of Dictionary<string,Object>, it'd be interesting to see where your code differs from it:
var data = new[]
{
new MyType()
{
TypeDetails = new Dictionary<string, object>() { { "foo", "bar" } }
},
new MyType()
{
TypeDetails = new Dictionary<string, object>() { { "baz", "qux" } }
},
};
var r = data.Select(d => d.TypeDetails);

KeyValuePair naming by ValueTuple in C# 7

Can the new feature in C# 7.0 (in VS 2017) to give tuple fields names be translated to KeyValuePairs?
Lets assume I have this:
class Entry
{
public string SomeProperty { get; set; }
}
var allEntries = new Dictionary<int, List<Entry>>();
// adding some keys with some lists of Entry
It would be nice to do something like:
foreach ((int collectionId, List<Entry> entries) in allEntries)
I have already added System.ValueTuple to the project.
Being able to write it like that would be much better than this traditional style:
foreach (var kvp in allEntries)
{
int collectionId = kvp.Key;
List<Entry> entries = kvp.Value;
}
Deconstruction requires a Deconstruct method defined either on the type itself, or as an extension method. KeyValuePaire<K,V> itself doesn't have a Deconstruct method, so you need to define an extension method:
static class MyExtensions
{
public static void Deconstruct<K,V>(this KeyValuePair<K,V> kvp, out K key, out V value)
{
key=kvp.Key;
value=kvp.Value;
}
}
This allows you to write:
var allEntries = new Dictionary<int, List<Entry>>();
foreach(var (key, entries) in allEntries)
{
...
}
For example:
var allEntries = new Dictionary<int, List<Entry>>{
[5]=new List<Entry>{
new Entry{SomeProperty="sdf"},
new Entry{SomeProperty="sdasdf"}
},
[11]=new List<Entry>{
new Entry{SomeProperty="sdfasd"},
new Entry{SomeProperty="sdasdfasdf"}
}, };
foreach(var (key, entries) in allEntries)
{
Console.WriteLine(key);
foreach(var entry in entries)
{
Console.WriteLine($"\t{entry.SomeProperty}");
}
}

How to modify HttpContext.Request.Form in asp.net core

I have an HttpContext.Request object that has data in the Form that is wrong and I want to fix it up and send the correct HttpContext on its way.
HttpContext.Request.Form is readonly, but if it wasn't I would have simply done the following;
HttpContext.Request.Form["a"] = "the correct value for a";
So, where is the best place in the pipeline to do this.
Is it possible to make the HttpContext.Request.Form write accessable via reflection?
This was easier than I thought. I am doing this in my middleware which is there to correct bad form data that came in.
public async Task Invoke(HttpContext context)
{
....
NameValueCollection fcnvc = context.Request.Form.AsNameValueCollection();
fcnvc.Set("a", "the correct value of a");
fcnvc.Set("b", "a value the client forgot to post");
Dictionary<string, StringValues> dictValues = new Dictionary<string, StringValues>();
foreach (var key in fcnvc.AllKeys)
{
dictValues.Add(key, fcnvc.Get(key));
}
var fc = new FormCollection(dictValues);
context.Request.Form = fc;
....
await _next.Invoke(context);
}
Interestingly the FormCollection is readonly, but the HttpContext.Request object is not thus allowing me to replace the entire Form.
Here's a .NET Core/5 solution that worked for me without using the Identity Server package.
Basically you build a new dictionary of type <string, StringValues> out of the existing form collection, modify the values in the dictionary how you want, then create a new FormCollection from that dictionary and set it to context.Request.Form. The important thing to remember is that the value which is of type StringValues is just an array of strings!
This example demonstrates me removing a "client_id" field from the request form.
var formDictionary = new Dictionary<string, StringValues>();
var form = context.Request.Form;
foreach (var key in form.Keys)
{
// Only add if key is NOT client_id
if (key != "client_id")
{
form.TryGetValue(key, out StringValues formValues);
formDictionary.Add(key, formValues);
}
}
FormCollection formCollection = new FormCollection(formDictionary);
context.Request.Form = formCollection;
Here is another example of me changing the "client_id" field to "NewValue"
var formDictionary = new Dictionary<string, StringValues>();
var form = context.Request.Form;
foreach (var key in form.Keys)
{
form.TryGetValue(key, out StringValues formValues);
// Change client_id value to "NewValue"
if (key == "client_id")
{
formValues = new string[] { "NewValue" };
}
formDictionary.Add(key, formValues);
}
FormCollection formCollection = new FormCollection(formDictionary);
context.Request.Form = formCollection;
AsNameValueCollection is inside of IdentityServer4.dll.
public static class IReadableStringCollectionExtensions
{
[DebuggerStepThrough]
public static NameValueCollection AsNameValueCollection(this IDictionary<string, StringValues> collection)
{
NameValueCollection values = new NameValueCollection();
foreach (KeyValuePair<string, StringValues> pair in collection)
{
string introduced3 = pair.get_Key();
values.Add(introduced3, Enumerable.First<string>(pair.get_Value()));
}
return values;
}
[DebuggerStepThrough]
public static NameValueCollection AsNameValueCollection(this IEnumerable<KeyValuePair<string, StringValues>> collection)
{
NameValueCollection values = new NameValueCollection();
foreach (KeyValuePair<string, StringValues> pair in collection)
{
string introduced3 = pair.get_Key();
values.Add(introduced3, Enumerable.First<string>(pair.get_Value()));
}
return values;
}
}
I personally prefer to use an extended method to do this.
public static IFormCollection PushToForm(this IFormCollection form, Dictionary<string, StringValues> data)
{
var formDictionary = new Dictionary<string, StringValues>();
foreach (var k in form.Keys)
{
form.TryGetValue(k, out StringValues v);
formDictionary.Add(k, v);
}
foreach (var x in data) formDictionary.Add(x.Key, x.Value);
return new FormCollection(formDictionary);
}
Example:
Request.Form = Request.Form.PushToForm(new Dictionary<string, StringValues>()
{
{ "key1", new string[] { "value1" } },
{ "key2", new string[] { "value2" } },
{ "key3", new string[] { "value3" } },
...
});
A bit complex but shorter solution
var collection = HttpContext.Request.Form;
var propInfo = collection.GetType().GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
propInfo.SetValue(collection, false, new object[]{});
collection.Remove("a");
collection.Add("a", "the correct value for a");
System.Diagnostics.Debug.Write(HttpContext.Request["a"]); // the correct value for a
Enjoy!

Serialize some string values in a Dictionary as integers

Currently my Dictionary<string, string> is serialized as:
{
"Uri" : "/site/Default.aspx",
"time-taken" : "232"
}
I would like Json.net to serialize it as
{
"Uri" : "/site/Default.aspx",
"time-taken" : 232
}
What would be the easiest way to achieve this with Json.net? I don't want to make a new class with the correct types instead of the Dictionary since the keys are many and may change. I know the keys that will be int.
I think I would just make a helper method that copied the data from a dictionary to a JObject like this:
public static class JsonHelper
{
public static string SerializeDictionary(Dictionary<string, string> dict, IEnumerable<string> intKeys)
{
JObject obj = new JObject();
foreach (var kvp in dict)
{
int intValue;
if (intKeys.Contains(kvp.Key) && int.TryParse(kvp.Value, out intValue))
obj.Add(kvp.Key, intValue);
else
obj.Add(kvp.Key, kvp.Value);
}
return obj.ToString(Formatting.Indented);
}
}
Then use it like this:
var dict = new Dictionary<string, string>();
dict.Add("AnInt", "123");
dict.Add("AString", "abc");
dict.Add("AnotherInt", "456");
dict.Add("KeepThisAsString", "789");
dict.Add("NotAnInt", "xyz");
var intKeys = new string[] { "AnInt", "AnotherInt" };
string json = JsonHelper.SerializeDictionary(dict, intKeys);
Console.WriteLine(json);
Output:
{
"AnInt": 123,
"AString": "abc",
"AnotherInt": 456,
"KeepThisAsString": "789",
"NotAnInt": "xyz"
}
Fiddle: https://dotnetfiddle.net/xdnnb0

copy dictionary containing object to an array of objects c#

I have a Dictionary like Dictionary<string,Object>,is there any method of converting the Dictionary to an array of objects,where the class of the object will contain two members-one of which will be the string and the other will be the Object stored as the value-pair in the dictionary.Please help!!!..
Dictionary<TKey, TValue> implements IEnumerable<T> where T is KeyValuePair<TKey, TValue>. To flatten this to an array all that is necessary is to call IEnuemrable<T>.ToArray as such:
Dictionary<string, int> dict = new Dictionary<string, int>() { { "Key1", 0 }, { "Key2", 1 } };
var kvArray = dict.ToArray();
kvArray will then be an array objects that reference the keys and values of each element in dict as two separate members of the same object.
Your question is a bit ambiguous though, perhaps further explanation would help us figure out a more appropriate solution.
Re your comment, LINQ is good for that:
Dictionary<string, int[]> dict = new Dictionary<string, int[]>() { { "Key1", new int[] { 0, 1, 2 } }, { "Key2", new int[] { 4, 5, 6 } } };
var pairs = dict.SelectMany(pair => pair.Value
.Select(v =>
new {
Key = pair.Key,
Value = v
}
)
);
Given a class:
class ClassA
{
string CustomerId { get; set; }
PatientRecords[] Records { get; set; }
public ClassA(string name, PatientRecords[] records)
{
Name = name;
Records = records;
}
}
I'm assuming that CollectionOfPatientRecords implements IEnumberable:
var dict = new Dictionary<string, CollectionOfPatientRecords> ( ... );
Then to get your array of ClassA with the right values:
dict.Select(kv => new ClassA(kv.Key, kv.Value.ToArray())).ToArray();

Categories