C# Dictionary with list as base - c#

I'm trying to create a custom dictionary that uses a List as a base (will be used in XML deserialization). I can't figure out how to create it as the key can't be put on string it seems.
The dictionary would have the key property of the TestObject as the Key and as Value it would have the TestObject itself.
public class TestObject
{
public string Key { get; set; }
public string Property1 { get; set; }
public int Property2 { get; set; }
}
public class CustomDictionary<string, TestObject> : List<TestObject>
{
}
public class Methods
{
public void TestMethod(List<TestObject> list)
{
var testObject = new TestObject()
{
Key = "TEST",
Property1 = "ABC",
Property2 = 123,
};
CustomDictionary<string, TestObject> dictionary = new CustomDictionary<string, TestObject>(list);
var test;
dictionary.TryGetValue(testObject.Key, out test);
}
}

Given your last comment, what you want to do is something like this:
public class Methods
{
public void TestMethod(List<TestObject> list)
{
Dictionary<string, TestObject> data = list.ToDictionary(x => x.Key);
}
}
This uses LINQ's ToDictionary method, although a simple foreach loop would suffice.

Related

C# foreach loop thru collection of unknown type

I have a generic method that can be called with 2 different object types, TypeA or TypeB. TypeA and TypeB are essentially identical classes except in name only. I am trying to determine how to prevent from having to duplicate the Foreach loop code for each object type. Is this possible ? thanks.
public class TypeA
{
public string Name { get; set; }
public string Department { get; set; }
public string Total { get; set; }
}
public class TypeB
{
public string Name { get; set; }
public string Department { get; set; }
public string Total { get; set; }
}
private CsvExport GenerateExport<T>(IEnumerable<T> scores)
{
CsvExport export = new CsvExport();
List<TypeA> aList = null;
List<TypeB> bList = null;
Type type = scores.GetType();
if (type.FullName.Contains("TypeA"))
{
aList = scores as List<ObjectaModel>;
}
else if (type.FullName.Contains("TypeB"))
{
bList = scores as List<ObjectbModel>;
}
foreach (var dt in aList)
{
export.AddRow();
export["Name"] = dt.Name;
export["Department"] = dt.Department;
export["Total "] = dt.Total;
};
return export;
}
In this particular case I strongly suggest you delegate the hard work to the CsvHelper library which you can also obtain from Nuget and is used like this...
public void ExportToCsv<T>(string filename, ImmutableArray<T> objects)
{
using (var writer = File.CreateText(filename))
{
var csv = new CsvWriter(writer);
csv.WriteRecords(objects);
}
}
The more general answer to your question is that you must either have both classes inherit from a common class or interface or you would have to use reflection to look for an obtain the values of the named properties.
Using a common interface...
public interface IScore
{
int HiScore {get;}
}
public class ScrabbleScore : IScore
{
public int HiScore {get;set;}
}
public class PacManScore : IScore
{
public int HiScore {get;set;}
}
public void Export<T>(IEnumerable<T> scores) where T: IScore
{
foreach(var s in scores)
{
CvsExport["Hi score"]= s.HiScore;
}
}
Using reflection...
var CsvExport = new Dictionary<string,string>();
foreach(var o in scores)
{
//note that checking the type for each object enables you to have heterogenous lists if you want
var objectType= o.GetType();
foreach(var p in objectType.GetProperties())
{
var propertyName = p.Name;
CsvExport[propertyName] = p.GetValue(o).ToString();
}
}
I would treat the reflection solution as the least favoured of the three.

Create MyItem class to be categorized by type

I have the following code, when I declare MyItem, it will be automatically added into Roster.
public class MyItem
{
private static Dictionary<string, string> Roster;
public static bool addToRoster(MyItem item)
{
if (Roster.ContainsKey(item.code))
return false;
Roster.Add(item.code, item.description);
return true;
}
public string code { get; protected set; }
public string description { get; protected set; }
public MyItem(string _code, string _desc)
{
code = _code;
description = _desc;
addToRoster(this);
}
}
So when I use it like below, Roster dictionary will contain 2 values:
MyItem itema = new MyItem("code1", "desc1");
MyItem itemb = new MyItem("code2", "desc2");
MyItem itemc = new MyItem("code1", "desc1");
So far so good. Now I may have multiple Rosters, based on different type. For example, I may have a Subject, a Room and a Building. I don't want to declare multiple classes derived from MyItem class, that will be too many. Of course I can add a property
string RosterType { get; set; }
in MyItem class, but that will make bunch of data redundancy.
I am thinking maybe I can use attribute, but it seems attribute cannot be used on object.
I am trying to find something like this
[RosterType("Room")]
MyItem item1("WJ203", "Room WJ203");
[RosterType("Subject")]
MyItem item2("MATH01", "Basic Math");
and, change the Roster to be
private static Dictionary<string, Dictionary<string, string>> Rosters;
Then I can add the items into different rosters based on different RosterType. But, it seems I cannot set attribute on object.
What should I do to archive my goal? Any good thought?
Let me add some details
The data I received is like:
{
result: [
{
"propA": { "code":"XXX", "desc":"XXX" },
"propB": { "code":"XXX", "desc":"XXX" },
"propC": { "code":"XXX", "desc":"XXX" }
},
{
"propA": { "code":"XXX", "desc":"XXX" },
"propB": { "code":"XXX", "desc":"XXX" },
"propC": { "code":"XXX", "desc":"XXX" }
},
{
"propA": { "code":"XXX", "desc":"XXX" },
"propB": { "code":"XXX", "desc":"XXX" },
"propC": { "code":"XXX", "desc":"XXX" }
}]
}
There are many different type of result, and most of them are combination of different type of props. all the props are in cod - desc format. I know it is a poor design, but nothing I can do from my side. I need to save the result into different tables, with just code; and also need to save all the code into different code tables. All these need to be done during desearlization. so I am hopping to do this in MyItem class. The categorize procedure maybe happen in OnDesearlization. So for the result part, I can easily customize my class based on different type of result.
The idea is, set the attribute to properties. Have a ResultBase class, which will go over the attributes, and if a Roster Item found, deal with it. So Once all this are done, I just need to declare my different Results with no extra coding...
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Reflection;
namespace RosterNS
{
using IRoster = IDictionary<string, string>;
[DataContract]
public class RosterItem
{
[DataMember]
public string code { get; set; }
[DataMember]
public string description { get; set; }
}
public class Rosters
{
static private Dictionary<string, IRoster> rosters = new Dictionary<string, IRoster>();
public static IRoster RegisterRoster(string category)
{
IRoster roster;
if (!rosters.TryGetValue(category, out roster))
{
roster = new Dictionary<string, string>();
rosters.Add(category, roster);
}
return roster;
}
#if DEBUG
public static void ListRosters()
{
foreach(KeyValuePair<string, IRoster> kvp in rosters)
{
Console.WriteLine("Category:{0}", kvp.Key.ToString());
foreach (KeyValuePair<string, string> skvp in kvp.Value)
Console.WriteLine("\t{0:20}==>{1}", skvp.Key, skvp.Value);
}
}
#endif
}
[AttributeUsage(AttributeTargets.Property)]
public class RosterCategoryAttribute : Attribute
{
private IRoster roster;
public string Category { get; protected set; }
public RosterCategoryAttribute(string category)
{
Category = category;
roster = Rosters.RegisterRoster(category);
}
public void addToRoster(RosterItem item)
{
if (item != null && !roster.ContainsKey(item.code))
roster.Add(item.code, item.description);
}
}
[DataContract]
public class RosterResult
{
//The only implementation that save the RosterItem into categorized Roster
[OnDeserialized]
public void OnDeserialized(StreamingContext sc)
{
PropertyInfo[] properties = GetType().GetProperties();
foreach (PropertyInfo info in properties)
{
RosterCategoryAttribute attr = info.GetCustomAttribute<RosterCategoryAttribute>();
if (attr != null)
attr.addToRoster(info.GetValue(this) as RosterItem);
}
}
}
}
To use it, I need to declare my Result like so
namespace MyResult
{
[DataContract]
public class ResultType1 : RosterResult
{
[DataMember]
[RosterCategory("Location")]
public RosterItem location { get; protected set; }
[DataMember]
[RosterCategory("Status")]
public RosterItem status { get; protected set; }
}
[DataContract]
public class ResultType2 : RosterResult
{
[DataMember]
[RosterCategory("Location")]
public RosterItem location { get; protected set; }
[DataMember]
[RosterCategory("Equipment")]
public RosterItem equipment { get; protected set; }
[DataMember]
public string Name { get; protected set; }
}
}
So after bunch of deserialization, the Roster will be filled up with unique code-spec pair, based on different categories.

Get list of properties in LINQ projection

I have a following LINQ expression:
var query = entities
.Select(e => new MyObject()
{
Property1 = e.Item1,
Property2 = e.Item2
});
MyObject might have also Property3, Property4 defined. I need to realize which properties are part of LINQ projection via expression visitor.
So I call something like:
var listOfProperties = query.GetSelectedPropertyNames();
and the content of listOfProperties will be string array which contains Property1, Property2 or something by which I can check:
var isPropertyInProjection = query.HasPropertyInProjection(nameof(MyObject.Property3));
and the result will be false.
You can easily do that using an ExpressionVisitor. Just create a new class and override the visiting methods. If you know that the projection was done using member bindings, you can simply override the method VisitMemberBinding and add the bound member to a list that you store as an instance variable. Then all you need to do is to make that instance variable public.
class ProjectionAnalyzer : ExpressionVisitor
{
private HashSet<MemberInfo> boundMembers = new HashSet<MemberInfo>();
protected override MemberBinding VisitMemberBinding(MemberBinding node)
{
boundMembers.Add(node.Member);
return base.VisitMemberBinding(node);
}
public IEnumerable<MemberInfo> BoundMembers => boundMembers;
}
Then, use this class as follows:
var analyzer = new ProjectionAnalyzer();
analyzer.Visit(selectorPredicate);
var boundMembers = analyzer.BoundMembers;
How you obtain the selector predicate depends on your LINQ provider.
I did something similar using VisitMemberAssignment:
namespace BoundPropertiesinQuery
{
static class IEnumerableExtensions
{
class ProjectedVisitor : ExpressionVisitor
{
public IList<string> ProjectedPropertyNames { get; set; } = new List<string>();
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
{
ProjectedPropertyNames.Add(node.Member.Name);
return base.VisitMemberAssignment(node);
}
}
public static IEnumerable<string> ProjectedProperties<T>(this IQueryable<T> #this)
{
var pv = new ProjectedVisitor();
pv.Visit(#this.Expression);
return pv.ProjectedPropertyNames.Distinct();
}
}
internal class MyObject
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
public int Property4 { get; set; }
}
internal class MyOtherObject
{
public int other1 { get; set; }
public int other2 { get; set; }
public int other3 { get; set; }
public int other4 { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var listOfItems = new List<MyOtherObject>()
{
new MyOtherObject
{
other1 = 1,
other2 = 2,
other3 = 3,
other4 = 4
},
new MyOtherObject
{
other1 = 5,
other2 = 6,
other3 = 7,
other4 = 8
}
};
var result = listOfItems.AsQueryable().Select(m => new MyObject
{
Property1 = m.other1,
Property2 = m.other2
}).ProjectedProperties();
foreach (var item in result)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
}

Inheritance from Jobject Newtonsoft

Inheritance from Jobject(Newtonsoft) the existents properties from class not serialized.
Why were the Id and Name properties not serialized?
public class Test : JObject
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
var test = new Test();
test["new_pro"] = 123456;
test.Id = 1;
test.Name = "Dog";
var r = Newtonsoft.Json.JsonConvert.SerializeObject(test);
// Result = { "new_pro":123456}
}
}
Any idea?
Whatever is the reason you want to do that - the reason is simple: JObject implements IDictionary and this case is treated in a special way by Json.NET. If your class implements IDictionary - Json.NET will not look at properties of your class but instead will look for keys and values in the dictionary. So to fix your case you can do this:
public class Test : JObject
{
public int Id
{
get { return (int) this["id"]; }
set { this["id"] = value; }
}
public string Name
{
get { return (string) this["name"]; }
set { this["name"] = value; }
}
}
If you just want to have both dynamic and static properties on your object - there is no need to inherit from JObject. Instead, use JsonExtensionData attribute:
public class Test {
public int Id { get; set; }
public string Name { get; set; }
[JsonExtensionData]
public Dictionary<string, JToken> AdditionalProperties { get; set; } = new Dictionary<string, JToken>();
}
var test = new Test();
test.AdditionalProperties["new_pro"] = 123456;
test.Id = 1;
test.Name = "Dog";
var r = Newtonsoft.Json.JsonConvert.SerializeObject(test);

Generic query in Linq

I want to make a generic search UserControl in wpf. I want it to get a collection of objects and a name of a property to search by.
The problem is I can't use generics because the code that calls the search function also can't know of the type.
Is there a way to achieve this? Or some way to query an Object that is underneath another type?
Consider this example.
interface IFoo
{
}
class Bar1 : IFoo
{
//interface implementations
public string Property1 { get; set; }
public string myProperty1 { set; get; }
}
class Bar2 : IFoo
{
//interface implementations
public string Property1 { get; set; }
public string myProperty1 { set; get; }
}
//Search the list of objects and access the original values.
List<IFoo> foos = new List<IFoo>();
foos.Add(new Bar1
{
Property1 = "bar1",
myProperty1 ="myBar1"
});
foos.Add(new Bar1());
foos.Add(new Bar2());
foos.Add(new Bar2());
//Get the objects.
foreach (var foo in foos)
{
//you can access foo directly without knowing the original class.
var fooProperty = foo.Property1;
//you have to use reflection to get the original type and its properties and methods
Type type = foo.GetType();
foreach (var propertyInfo in type.GetProperties())
{
var propName = propertyInfo.Name;
var propValue = propertyInfo.GetValue(foo);
}
}
var result = list.Where(a => a.propertyName);
You can use reflection
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var Data = new List<object>() { new A() { MyProperty = "abc" }, new B() { MyProperty = "cde"} };
var Result = Data.Where(d => (d.GetType().GetProperty("MyProperty").GetValue(d) as string).Equals("abc"));
// Result is IEnumerable<object> wich contains one A class object ;)
}
}
class A
{
public string MyProperty { get; set; }
}
class B
{
public string MyProperty { get; set; }
}
}

Categories