Replace property values in a class from List<Dictionary> values - c#

I have a method that takes a List<Dictionary<string,object>> as a parameter. The plan is to use that parameter, but only update the values held in a particular class. Here is the (partially written) method
public async Task<Errors> UpdatePageForProject(Guid projectId, List<Dictionary<string, object>> data)
{
if (!IsValidUserIdForProject(projectId))
return new Errors { ErrorMessage = "Project does not exist", Success = false };
if (data.Count == 0)
return new Errors { ErrorMessage = "No data passed to change", Success = false };
var page = await _context.FlowPages.FirstOrDefaultAsync(t => t.ProjectId == projectId);
foreach (var d in data)
{
}
return new Errors { Success = true };
}
My original plan is to take each dictionary, check if the key and the property in page match and then alter the value (so I can pass in 1 dictionary or 8 dictionaries in the list and then alter page to save back to my entity database).
I'd rather not use reflection due to the speed hit (though C#9 is really fast, I'd still rather not use it), but I'm not sure how else this can be done. I did consider using AutoMapper to do this, but for now would rather not (it's a PoC, so it is possibly overkill)

If you want to do this without Reflection (which I agree is a good idea, not just for performance reasons) then you could use a "map" or lookup table with actions for each property.
var map = new Dictionary<string,Action<Page,object>>()
{
{ "Title", (p,o) => p.Title = (string)o },
{ "Header", (p,o) => p.Field1 = (string)o },
{ "DOB", (p,o) => p.DateOfBirth = (DateTime)o }
};
You can then iterate over your list of dictionaries and use the map to execute actions that update the page.
foreach (var dictionary in data)
{
foreach (entry in dictionary)
{
var action = map[entry.Key];
action(page, entry.Value);
}
}

Related

.Net Core, merge two lists, remove invalid entries and keep values in the original list

I have a default list of attributes and in incoming list of attributes. I need to remove any items from the incoming list that do now match the Name in the default list. I have tried many different LINQ queries, but have not been able to accomplish this task.
Default List:
Attributes[] defaultAttributes =
{
new ProfileAttributes() { Name = "FirstName", Include = false, Required = false },
new ProfileAttributes() { Name = "MiddleName", Include = false, Required = false },
new ProfileAttributes() { Name = "HomeCountry", Include = false, Required = false },
...
I want to merge the two lists and remove any items where the Name of the incoming list does not match the default list.
For example in the following remove Favorite color because it is an invalid name and preserve the required values.
Attributes[] incomingAttributes =
{
new ProfileAttributes() { Name = "FavoriteColor", Required = true },
new ProfileAttributes() { Name = "MiddleName", Required = false},
new ProfileAttributes() { Name = "HomeCountry", Required = true },
Most incoming lists will not have "Include" So I need to add that and set it to true if it is in the incoming list, otherwise false. I have done that with the following, but interested if there is a way to combine this with the merge.
Revised, I used the following solution:
I used lists instead of array lists. I found this easier to loop through and bind to checkboxes on the form
Attributes[] defaultAttributes
to
List<ProfileAttributes> defaultAttributes = new List<ProfileAttributes>()
Inside the loop for my form:
<input type="checkbox"for="myModel.ProfileAttributes[i].Include"
I created an empty list:
List<ProfileAttributes> validListAttributes = new();
Then I created a loop. If the name is valid add it to the empty list and add the Include attribute:
foreach (var a in myModel.ProfileAttributes) //incomingAttributes
{
if (defaultAttributes.Any(d => d.Name == a.Name))
{
a.Include = true;
validListAttributes.Add(a);
}
}
Then another loop to add missing attributes because all attributes must be display on the form:
foreach (var d in defaultAttributes)
{
if (!validListAttributes.Any(v => v.Name == d.Name))
{
validListAttributes.Add(d);
}
}
Then update the model with the valid list containing all attributes:
myModel.ProfileAttributes = validListAttributes.ToList();
This will be a lot easier with a generic IEqualityComparer whose job is to compare the Name property of the instances involved in the process.
So let's define an IEqualityComparer for the Attributes class
public class ProfileAttributesComparer : IEqualityComparer<ProfileAttributes>
{
public bool Equals(ProfileAttributes obj1, ProfileAttributes obj2)
{
if(obj1 == null && obj2 == null)
return true;
if(obj1 == null || obj2 == null)
return false;
var result = string.Compare(obj1.Name, obj2.Name,
StringComparison.CurrentCultureIgnoreCase);
return result == 0;
}
public int GetHashCode(ProfileAttributes obj)
{
return obj.Name.GetHashCode();
}
}
Now you can process the elements in the incomingAttributes that have a name equal to an instance inside the defaultAttributes and change the property Include to true
var result = incomingAttributes
.Where(x => defaultAttributes.Contains(x, new ProfileAttributesComparer()))
.Select(p => { p.Include = true; return p;});
The result variable contains only the items with the same name but the incomingAttributes list still has three items and two of them have the Include property changed to true.

How to easily merge two anonymous objects with different data structure?

I would like to merge these two anonymous objects:
var man1 = new {
name = new {
first = "viet"
},
age = 20
};
var man2 = new {
name = new {
last = "vo"
},
address = "123 street"
};
Into a single one:
var man = new {
name = new {
first = "viet",
last = "vo"
},
age = 20,
address = "123 street"
};
I looked for a solution but found nothing clever.
Convert the anonymous object to ExpandoObject which is essentially a dictionary of string key and object value:
var man1Expando = man1.ToDynamic();
var man2Expando = man2.ToDynamic();
public static ExpandoObject ToDynamic(this object obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var propertyInfo in obj.GetType().GetProperties())
{
var currentValue = propertyInfo.GetValue(obj);
if (propertyInfo.PropertyType.IsAnonymous())
{
expando.Add(propertyInfo.Name, currentValue.ToDynamic());
}
else
{
expando.Add(propertyInfo.Name, currentValue);
}
}
return expando as ExpandoObject;
}
I'm using a helper extension to establish whether a type is an anonymous one:
public static bool IsAnonymous(this Type type)
{
return type.DeclaringType is null
&& type.IsGenericType
&& type.IsSealed
&& type.IsClass
&& type.Name.Contains("Anonymous");
}
Then, merge two resulting expando objects into one, but recursively, checking for nested expando objects:
var result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
public static IDictionary<string, object> MergeDictionaries(
IDictionary<string, object> targetDictionary,
IDictionary<string, object> sourceDictionary,
bool overwriteTarget)
{
foreach (var pair in sourceDictionary)
{
if (!targetDictionary.ContainsKey(pair.Key))
{
targetDictionary.Add(pair.Key, sourceDictionary[pair.Key]);
}
else
{
if (targetDictionary[pair.Key] is IDictionary<string, object> innerTargetDictionary)
{
if (pair.Value is IDictionary<string, object> innerSourceDictionary)
{
targetDictionary[pair.Key] = MergeDictionaries(
innerTargetDictionary,
innerSourceDictionary,
overwriteTarget);
}
else
{
// What to do when target propety is nested, but source is not?
// Who takes precedence? Target nested property or source value?
if (overwriteTarget)
{
// Replace target dictionary with source value.
targetDictionary[pair.Key] = pair.Value;
}
}
}
else
{
if (pair.Value is IDictionary<string, object> innerSourceDictionary)
{
// What to do when target propety is not nested, but source is?
// Who takes precedence? Target value or source nested value?
if (overwriteTarget)
{
// Replace target value with source dictionary.
targetDictionary[pair.Key] = innerSourceDictionary;
}
}
else
{
// Both target and source are not nested.
// Who takes precedence? Target value or source value?
if (overwriteTarget)
{
// Replace target value with source value.
targetDictionary[pair.Key] = pair.Value;
}
}
}
}
}
return targetDictionary;
}
The overwriteTarget parameter decides which object takes priority when merging.
Usage code:
var man1 = new
{
name = new
{
first = "viet",
},
age = 20,
};
var man2 = new
{
name = new
{
last = "vo",
},
address = "123 street",
};
var man1Expando = man1.ToDynamic();
var man2Expando = man2.ToDynamic();
dynamic result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
and the result:
{
"name": {
"first": "viet",
"last": "vo"
},
"age": 20,
"address": "123 street"
}
Notice how I assigned the result to dynamic. Leaving compiler assign the type will leave you with expando object presented as IDictionary<string, object>. With a dictionary representation, you cannot access properties in the same manner as if it was an anonymous object:
var result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
result.name; // ERROR
That's why the dynamic. With dynamic you are losing compile time checking, but have two anonymous objects merged into one. You have to judge for yourself if it suits you.
There's nothing built-in in the C# language to support your use case. Thus, the question in your title needs to be answered with "Sorry, there is no easy way".
I can offer the following alternatives:
Do it manually:
var man = new {
name = new {
first = man1.name.first,
last = man2.name.first
},
age = man1.age,
address = man2.address
};
Use a class instead of an anonymous type for the resulting type (let's call it CompleteMan). Then, you can
create a new instance var man = new CompleteMan(); ,
use reflection to collect the properties and values from your "partial men" (man1 and man2),
assign those values to the properties of your man.
It's "easy" in the sense that the implementation will be fairly straight-forward, but it will still be a lot of code, and you need to take your nested types (name) into account.
If you desperately want to avoid non-anonymous types, you could probably use an empty anonymous target object, but creating this object (var man = new { name = new { first = (string)null, last = (string)null, ...) is not really less work than creating a class in the first place.
Use a dedicated dynamic data structure instead of anonymous C# classes:
The Newtonsoft JSON library supports merging of JSON objects.
Dictionaries can also be merged easily.
ExpandoObjects can be merged easily as well.

Arbitrary Length ORDER BYs using NHibernate

If I were doing this using PHP and MySQL, it would look something like this (disclaimer that this PHP code is not suitable for external/web-facing use, as it's vulnerable to SQL injection):
<?php
function orderByColumns ($columns, $sql) {
if (0 < count($columns)) {
$column = array_shift($columns);
if (! stripos($sql, "ORDER BY")) {
$sql .= " ORDER BY";
}
$sql .= " {$column['name']} {$column['dir']}";
$sql .= 0 < count($columns) ? "," : "";
return orderByColumns($columns, $sql);
}
return $sql;
}
$columns = array(
array(
"name" => "foo",
"dir" => "ASC"
),
array(
"name" => "bar",
"dir" => "DESC"
)
);
$sql = "SELECT * FROM baz";
$sql = orderByColumns($columns, $sql); // And from here I could make my query
The point is that $columns is to be an input from a user somewhere, and that that could be used to order the columns without knowing the list in advance, and in a method that is reusable.
I'm looking for a way to do something similar using C# and specifically NHibernate, but it doesn't really seem to work. Here is something along the lines of what I've been trying in C#:
List<string> columns = new List<string>()
{
"Column1",
"Column2",
"Column3"
// And there could be more.
}
string column = columns.First();
fq = foo.Queryable.OrderBy(
i => i.GetType().GetProperty(column).GetValue(i, null)
);
foreach (string column in columns)
{
fq = fq.ThenBy(
i => i.GetType().GetProperty(column).GetValue(i, null)
);
}
And, I've looked at a few StackOverflow answers (ok, more than a few), but they don't seem to be addressing how to build NHibernate queries dynamically in the way I'm looking for. The one that felt most promising is Dynamic QueryOver in nHibernate, but I'm having a hard time fully grokking whether that's even in the right direction.
So, the problem where is that you aren't executing anything at this point, so nhibernate is going to try to translate that to SQL, which is going to complain because it doesn't know about the GetType() method.
You'd have to build up your own Expression instance, and there aren't great ways of doing that dynamically, though it can be done, but still not fun to do.
I think it'd be easier to make a dictionary of lambda expressions and columns
var lookup = new Dictionary<string, Expression<Func<T, object>>> {
{ "ColumnA", x => x.ColumnA },
{ "ColumnB", x => x.ColumnB }
};
foreach (string column in columns) {
fq = fq.ThenBy(lookup[column]);
}
Even then, this might not work if it complains about Expression<Func<T,object>>
I was intrigued by this question and wanted to take a crack at making #DarrenKopp's answer generic. My code got more long-winded than I expected, but I believe it does work. I tested with Linq to Objects, so nHibernate's Linq provider is untested.
The code is available here.
You can call to it with something like this...
var sortedItems = items.OrderBy(
new OrderByKeyInfo ("MyPropertyA", OrderByDirection.Descending),
new OrderByKeyInfo ("MyPropertyB", OrderByDirection.Ascending),
new OrderByKeyInfo ("MyPropertyC", OrderByDirection.Ascending));
Here's a quick proof of concept around dynamic sort conditions. You might find that avoiding trips to the database via NHibernate may be better, as it may be confusing to the user if the initial sort contains, for example, 8 records, but sorting the data again returns 9, as a new record was added in between and is now displayed as we've gone back to the DB rather than just re-sorting the in-memory collection - and I'm not sure if/how this would map to NHibernate anyway.
This is a quick and dirty solution for a console application, simply to prove that it'll work, there'll be a few tweaks and optimisations available no doubt. Ultimately, the overload
List<T>.Sort(Comparison<T>)
is the one that will prevent having to dynamically create a class that implements IComparer of T:
class Program
{
private class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int NumberOfChildren { get; set; }
}
private static List<Person> people = new List<Person>()
{
new Person() { Name="Andrew", Age=35, NumberOfChildren=3},
new Person() { Name="Maria",Age=33,NumberOfChildren=3},
new Person() {Name="Tim",Age=67,NumberOfChildren=4},
new Person() {Name="Tim",Age=62,NumberOfChildren=2},
new Person() {Name="Jim", Age=67,NumberOfChildren=2},
new Person() {Name="Tim",Age=33,NumberOfChildren=0},
new Person() {Name="Bob",Age=35,NumberOfChildren =3},
new Person() {Name="Daisy",Age=1,NumberOfChildren=0}
};
static void Main(string[] args)
{
List<string> sortConditions = new List<string>() { "Age", "Name", "NumberOfChildren" };
var properties = GetSortProperties<Person>(sortConditions);
people.Sort((Person a, Person b) =>
{
int result = 0;
foreach (PropertyInfo prop in properties)
{
result = ((IComparable)prop.GetValue(a, null)).CompareTo(prop.GetValue(b, null));
if (result != 0)
break;
}
return result;
});
}
static List<PropertyInfo> GetSortProperties<T>(List<string> propertyNames)
{
List<PropertyInfo> properties = new List<PropertyInfo>();
var typeProperties = typeof(T).GetProperties();
foreach (string propName in propertyNames)
{
properties.Add(typeProperties.SingleOrDefault(tp => tp.Name == propName));
}
return properties;
}
}

Alternative to if, else if

I have a lot of if, else if statements and I know there has to be a better way to do this but even after searching stackoverflow I'm unsure of how to do so in my particular case.
I am parsing text files (bills) and assigning the name of the service provider to a variable (txtvar.Provider) based on if certain strings appear on the bill.
This is a small sample of what I'm doing (don't laugh, I know it's messy). All in all, There are approximately 300 if, else if's.
if (txtvar.BillText.IndexOf("SWGAS.COM") > -1)
{
txtvar.Provider = "Southwest Gas";
}
else if (txtvar.BillText.IndexOf("georgiapower.com") > -1)
{
txtvar.Provider = "Georgia Power";
}
else if (txtvar.BillText.IndexOf("City of Austin") > -1)
{
txtvar.Provider = "City of Austin";
}
// And so forth for many different strings
I would like to use something like a switch statement to be more efficient and readable but I'm unsure of how I would compare the BillText. I'm looking for something like this but can't figure out how to make it work.
switch (txtvar.BillText)
{
case txtvar.BillText.IndexOf("Southwest Gas") > -1:
txtvar.Provider = "Southwest Gas";
break;
case txtvar.BillText.IndexOf("TexasGas.com") > -1:
txtvar.Provider = "Texas Gas";
break;
case txtvar.BillText.IndexOf("Southern") > -1:
txtvar.Provider = "Southern Power & Gas";
break;
}
I'm definitely open to ideas.
I would need the ability to determine the order in which the values were evaluated.
As you can imagine, when parsing for hundreds of slightly different layouts I occasionally run into the issue of not having a distinctly unique indicator as to what service provider the bill belongs to.
Why not use everything C# has to offer? The following use of anonymous types, collection initializers, implicitly typed variables, and lambda-syntax LINQ is compact, intuitive, and maintains your modified requirement that patterns be evaluated in order:
var providerMap = new[] {
new { Pattern = "SWGAS.COM" , Name = "Southwest Gas" },
new { Pattern = "georgiapower.com", Name = "Georgia Power" },
// More specific first
new { Pattern = "City of Austin" , Name = "City of Austin" },
// Then more general
new { Pattern = "Austin" , Name = "Austin Electric Company" }
// And for everything else:
new { Pattern = String.Empty , Name = "Unknown" }
};
txtVar.Provider = providerMap.First(p => txtVar.BillText.IndexOf(p.Pattern) > -1).Name;
More likely, the pairs of patterns would come from a configurable source, such as:
var providerMap =
System.IO.File.ReadLines(#"C:\some\folder\providers.psv")
.Select(line => line.Split('|'))
.Select(parts => new { Pattern = parts[0], Name = parts[1] }).ToList();
Finally, as #millimoose points out, anonymous types are less useful when passed between methods. In that case we can define a trival Provider class and use object initializers for nearly identical syntax:
class Provider {
public string Pattern { get; set; }
public string Name { get; set; }
}
var providerMap =
System.IO.File.ReadLines(#"C:\some\folder\providers.psv")
.Select(line => line.Split('|'))
.Select(parts => new Provider() { Pattern = parts[0], Name = parts[1] }).ToList();
Since you seem to need to search for the key before returning the value a Dictionary is the right way to go, but you will need to loop over it.
// dictionary to hold mappings
Dictionary<string, string> mapping = new Dictionary<string, string>();
// add your mappings here
// loop over the keys
foreach (KeyValuePair<string, string> item in mapping)
{
// return value if key found
if(txtvar.BillText.IndexOf(item.Key) > -1) {
return item.Value;
}
}
EDIT: If you wish to have control over the order in which elemnts are evaluated, use an OrderedDictionary and add the elements in the order in which you want them evaluated.
One more using LINQ and Dictionary
var mapping = new Dictionary<string, string>()
{
{ "SWGAS.COM", "Southwest Gas" },
{ "georgiapower.com", "Georgia Power" }
.
.
};
return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
.Select(pair => pair.Value)
.FirstOrDefault();
If we prefer empty string instead of null when no key matches we can use the ?? operator:
return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
.Select(pair => pair.Value)
.FirstOrDefault() ?? "";
If we should consider the dictionary contains similar strings we add an order by, alphabetically, shortest key will be first, this will pick 'SCE' before 'SCEC'
return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
.OrderBy(pair => pair.Key)
.Select(pair => pair.Value)
.FirstOrDefault() ?? "";
To avoid the blatant Schlemiel the Painter's approach that looping over all the keys would involve: let's use regular expressions!
// a dictionary that holds which bill text keyword maps to which provider
static Dictionary<string, string> BillTextToProvider = new Dictionary<string, string> {
{"SWGAS.COM", "Southwest Gas"},
{"georgiapower.com", "Georgia Power"}
// ...
};
// a regex that will match any of the keys of this dictionary
// i.e. any of the bill text keywords
static Regex BillTextRegex = new Regex(
string.Join("|", // to alternate between the keywords
from key in BillTextToProvider.Keys // grab the keywords
select Regex.Escape(key))); // escape any special characters in them
/// If any of the bill text keywords is found, return the corresponding provider.
/// Otherwise, return null.
string GetProvider(string billText)
{
var match = BillTextRegex.Match(billText);
if (match.Success)
// the Value of the match will be the found substring
return BillTextToProvider[match.Value];
else return null;
}
// Your original code now reduces to:
var provider = GetProvider(txtvar.BillText);
// the if is be unnecessary if txtvar.Provider should be null in case it can't be
// determined
if (provider != null)
txtvar.Provider = provider;
Making this case-insensitive is a trivial exercise for the reader.
All that said, this does not even pretend to impose an order on which keywords to look for first - it will find the match that's located earliest in the string. (And then the one that occurs first in the RE.) You do however mention that you're searching through largeish texts; if .NET's RE implementation is at all good this should perform considerably better than 200 naive string searches. (By only making one pass through the string, and maybe a little by merging common prefixes in the compiled RE.)
If ordering is important to you, you might want to consider looking for an implementation of a better string search algorithm than .NET uses. (Like a variant of Boyer-Moore.)
What you want is a Dictionary:
Dictionary<string, string> mapping = new Dictionary<string, string>();
mapping["SWGAS.COM"] = "Southwest Gas";
mapping["foo"] = "bar";
... as many as you need, maybe read from a file ...
Then just:
return mapping[inputString];
Done.
One way of doing it (other answers show very valid options):
void Main()
{
string input = "georgiapower.com";
string output = null;
// an array of string arrays...an array of Tuples would also work,
// or a List<T> with any two-member type, etc.
var search = new []{
new []{ "SWGAS.COM", "Southwest Gas"},
new []{ "georgiapower.com", "Georgia Power"},
new []{ "City of Austin", "City of Austin"}
};
for( int i = 0; i < search.Length; i++ ){
// more complex search logic could go here (e.g. a regex)
if( input.IndexOf( search[i][0] ) > -1 ){
output = search[i][1];
break;
}
}
// (optional) check that a valid result was found.
if( output == null ){
throw new InvalidOperationException( "A match was not found." );
}
// Assign the result, output it, etc.
Console.WriteLine( output );
}
The main thing to take out of this exercise is that creating a giant switch or if/else structure is not the best way to do it.
There are several approaches to do this, but for the reason of simplicity, conditional operator may be a choice:
Func<String, bool> contains=x => {
return txtvar.BillText.IndexOf(x)>-1;
};
txtvar.Provider=
contains("SWGAS.COM")?"Southwest Gas":
contains("georgiapower.com")?"Georgia Power":
contains("City of Austin")?"City of Austin":
// more statements go here
// if none of these matched, txtvar.Provider is assigned to itself
txtvar.Provider;
Note the result is according to the more preceded condition which is met, so if txtvar.BillText="City of Austin georgiapower.com"; then the result would be "Georgia Power".
you can use dictionary.
Dictionary<string, string> textValue = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> textKey in textValue)
{
if(txtvar.BillText.IndexOf(textKey.Key) > -1)
return textKey.Value;
}

Map enum value robustly

I have a form where I collect data from users. When this data is collected, I pass it to various partners, however each partner has their own rules for each piece of data, so this has to be converted. I can make this happen, but my worries are about the robustness. Here's some code:
First, I have an enum. This is mapped to dropdown a dropdown list - the description is the text value, and the int mapped to the value.
public enum EmploymentStatusType
{
[Description("INVALID!")]
None = 0,
[Description("Permanent full-time")]
FullTime = 1,
[Description("Permanent part-time")]
PartTime = 2,
[Description("Self employed")]
SelfEmployed = 3
}
When the form is submitted, the selected value is converted to its proper type and stored in another class - the property looks like this:
protected virtual EmploymentStatusType EmploymentStatus
{
get { return _application.EmploymentStatus; }
}
For the final bit of the jigsaw, I convert the value to the partners required string value:
Dictionary<EmploymentStatusType, string> _employmentStatusTypes;
Dictionary<EmploymentStatusType, string> EmploymentStatusTypes
{
get
{
if (_employmentStatusTypes.IsNull())
{
_employmentStatusTypes = new Dictionary<EmploymentStatusType, string>()
{
{ EmploymentStatusType.FullTime, "Full Time" },
{ EmploymentStatusType.PartTime, "Part Time" },
{ EmploymentStatusType.SelfEmployed, "Self Employed" }
};
}
return _employmentStatusTypes;
}
}
string PartnerEmploymentStatus
{
get { return _employmentStatusTypes.GetValue(EmploymentStatus); }
}
I call PartnerEmploymentStatus, which then returns the final output string.
Any ideas how this can be made more robust?
Then you need to refactor it into one translation area. Could be something like a visitor pattern implementation. Your choices are distribute the code (as you are doing now) or visitor which would centralize it. You need to build in a degree of fragility so your covering tests will show problems when you extend in order to force you to maintain the code properly. You are in a fairly common quandry which is really a code organisational one
I did encounter such a problem in one of my projects and I solved it by using a helper function and conventions for resource names.
The function is this one:
public static Dictionary<T, string> GetEnumNamesFromResources<T>(ResourceManager resourceManager, params T[] excludedItems)
{
Contract.Requires(resourceManager != null, "resourceManager is null.");
var dictionary =
resourceManager.GetResourceSet(culture: CultureInfo.CurrentUICulture, createIfNotExists: true, tryParents: true)
.Cast<DictionaryEntry>()
.Join(Enum.GetValues(typeof(T)).Cast<T>().Except(excludedItems),
de => de.Key.ToString(),
v => v.ToString(),
(de, v) => new
{
DictionaryEntry = de,
EnumValue = v
})
.OrderBy(x => x.EnumValue)
.ToDictionary(x => x.EnumValue, x => x.DictionaryEntry.Value.ToString());
return dictionary;
}
The convention is that in my resource file I will have properties that are the same as enum values (in your case None, PartTime etc). This is needed to perform the Join in the helper function which, you can adjust to match your needs.
So, whenever I want a (localized) string description of an enum value I just call:
var dictionary = EnumUtils.GetEnumNamesFromResources<EmploymentStatusType>(ResourceFile.ResourceManager);
var value = dictionary[EmploymentStatusType.Full];

Categories