How to store Multiple derived class in same list? - c#

I have these classes
public class SubMenuItem : SubMenuVariant
{
public string SubMenuTitle { get; set; }
public LinkFieldType Link { get; set; }
public List<SubMenuSubItem> SubItems { get; set; }
}
public class SubMenuHighlightItem : SubMenuVariant
{
[JsonPropertyName(FieldNames.HighlightTitle)]
public string HighlightTitle { get; set; }
[JsonPropertyName(FieldNames.HighlightText)]
public string HighlightText { get; set; }
[JsonPropertyName(FieldNames.HighlightText)]
public Link HighLightLink { get; set; }
}
public class SubMenuVariant
{
}
Which I currently store in a List<SubMenuVariant> submenu
Problem is though I am not able to access the individual properties the different menues have, since they are being casted to a SubMenuVariant, which don't have any properties.
The list can only contain one type, at no point will both types exist in the list. The items is not being added explicitly to the list, but is being created by JsonSerializer.Deserialize a json request, which contains the properties, to the baseclass.
So the json can either look like this:
{
"submenu": [
{
"SubMenuTitle " : "Title",
"Link" : "Link",
"SubItems" : [
{...}
]
}
]
}
Or
{
"submenu": [
{
"HighlightTitle " : "Title",
"HighlightLink" : "Link",
"HighlightText" : "Text"
}
]
}
Is it somehow possible to store different class types in the same list?

Your issue is not that you can't store different types derived from the same base class. Your problem is accessing the members of the run-time types of the objects. That requires a cast. You can conditionally cast the items as you get them out of the list:
foreach (var smv in submenu)
{
var smi = smv as SubMenuItem;
if (smi != null)
{
// ...
}
else
{
var smhi = smv as SubMenuHighlightItem;
if (smhi != null)
{
// ...
}
}
}
In newer versions of C#, you can use pattern-matching:
foreach (var smv in submenu)
{
if (smv is SubMenuItem smi)
{
// ...
}
else if (smv is SubMenuHighlightItem smhi)
{
// ...
}
}
Here's an example of the pattern-matching option in action:
class Program
{
static void Main(string[] args)
{
var items = new List<BaseType>();
items.Add(new FirstDerivedType { FirstName = "One" });
items.Add(new SecondDerivedType { SecondName = "Two" });
items.Add(new FirstDerivedType { FirstName = "Three" });
items.Add(new SecondDerivedType { SecondName = "Four" });
foreach (var bt in items)
{
if (bt is FirstDerivedType fdt)
{
Console.WriteLine(fdt.FirstName);
}
else if (bt is SecondDerivedType sdt)
{
Console.WriteLine(sdt.SecondName);
}
}
}
}
public class FirstDerivedType : BaseType
{
public string FirstName { get; set; }
}
public class SecondDerivedType : BaseType
{
public string SecondName { get; set; }
}
public class BaseType
{
}

No, your solution is as good as it gets. The only other - worse - option being List<object>.

You can also try reflection, if you know the property name you can
access it as follows:
internal class Program
{
static void Main(string[] args)
{
List<SubMenuVariant> variants = new List<SubMenuVariant>();
variants.Add(new Sub1() { Title = "Test" });
variants.Add(new Sub2());
var prop = variants.First().GetType().GetProperty("Title");
prop?.GetValue(variants.First(), null);
}
}
public class Sub1 :SubMenuVariant
{
public string Title { get; set; }
}
public class Sub2: SubMenuVariant
{
public int Index { get; set; }
}
public class SubMenuVariant
{
}
This will generate the following result:

Related

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.

Cast generic collection item type

The following code illustrates a situation I'm having. The real code use different names and get values in other ways, but they match with what I need an answer. Specifically in lines 76-89 (the only lines I control) I need to extract a variable of type "ICollection" with the values and I don't like none of the used approaches. Is there another approach to do it without to create class "AbstractCollection"?
namespace ConsoleApp1
{
using System.Collections.Generic;
using System.Linq;
interface IEntity
{
string Id { get; }
string Name { get; }
}
class Entity : IEntity
{
public Entity(string id, string name)
{
Id = id;
Name = name;
}
public string Id { get; }
public string Name { get; }
}
interface ICollection<TGeneric>
{
IEnumerable<TGeneric> Items { get; }
}
class Collection<TGeneric> : ICollection<TGeneric> where TGeneric : Entity, IEntity
{
public IEnumerable<TGeneric> Items { get; set; }
}
class AbstractCollection<TConcrete, TAbstract> : ICollection<TAbstract> where TAbstract : class, IEntity
{
public AbstractCollection(ICollection<TConcrete> collection)
{
this._Items = new List<TAbstract>();
if (collection?.Items != null)
{
foreach (TConcrete concreteItem in collection.Items)
{
TAbstract abstractItem = concreteItem as TAbstract;
this._Items.Add(abstractItem);
}
}
}
public IEnumerable<TAbstract> Items
{
get { return this._Items; }
set { this._Items = value?.ToList(); }
}
private IList<TAbstract> _Items { get; set; }
}
class EntityCollection : Collection<Entity>
{
public EntityCollection()
{
var items = new List<Entity>()
{
new Entity("1", "Name1"),
new Entity("2", "Name2"),
new Entity("3", "Name3")
};
Items = items;
}
}
class Context
{
public Context()
{
var concreteItems = new EntityCollection();
// I can modify from this line to the end of the method but not any code before.
// I expected values in "list1" but is null.
var list1 = concreteItems as ICollection<IEntity>;
var list2 = concreteItems as ICollection<Entity>;
var abstractItems = new List<IEntity>();
foreach (Entity concreteItem in concreteItems.Items)
{
IEntity abstractItem = concreteItem as IEntity;
abstractItems.Add(abstractItem);
}
// Why "list3" is null?
var list3 = abstractItems as ICollection<IEntity>;
// I want to avoid class "AbstractCollection"
var list4 = new AbstractCollection<Entity, IEntity>(list2);
// Finally "list5" has value in the way I want it.
var list5 = list4 as ICollection<IEntity>;
}
}
class Program
{
static void Main(string[] args)
{
var context = new Context();
}
}
}
Covariance guides to the solution:
interface ICollection<out TGeneric>
{
IEnumerable<TGeneric> Items { get; }
}

C# Accessing custom attribute of owner object

How can I access the custom attribute of the parent or owner object.
Look at the FieldInfo property of the SQLFieldInfo struct
Here's a more detailed program that will compile and run that shows what I need.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Employee myclass = new Employee();
// Load from sql server...
myclass.Name = "Alain";
myclass.Age = 51;
//----
MessageBox.Show(myclass.Name.ToString()); // Should return Alain
MessageBox.Show(myclass.Age.FieldInfo.Type.ToString()); // Should output "int"
}
}
// This next class is generated by a helper exe that reads SQL table design and create the class from it
[SQLTableAttribute(DatabaseName = "Employees", Schema = "dbo", TableName = "Employees")]
public class Employee
{
[SQLFieldAttribute(FieldName = "ID", Type = SqlDbType.Int)]
public SQLFieldInfo<int> ID { get; set; }
[SQLFieldAttribute(FieldName = "Name", Type = SqlDbType.NVarChar, Size = 200)]
public SQLFieldInfo<String> Name { get; set; }
[SQLFieldAttribute(FieldName = "Age", Type = SqlDbType.Int)]
public SQLFieldInfo<int> Age { get; set; }
}
public struct SQLFieldInfo<T>
{
private readonly T value;
public SQLFieldInfo(T Value)
{
this.value = Value;
}
public static implicit operator SQLFieldInfo<T>(T Value)
{
return new SQLFieldInfo<T>(Value);
}
public T Value
{
get
{
return this.value;
}
}
public override string ToString()
{
return this.value.ToString();
}
public SQLFieldAttribute FieldInfo
{
get
{
// Need to retreive the attribute class of the parent or declaring member
return null;
}
}
}
// Holds the sql field information
public class SQLFieldAttribute : Attribute
{
public string FieldName { get; set; }
public SqlDbType Type { get; set; }
public bool AllowNull { get; set; }
public int Size { get; set; }
}
// Holds the sql table information
public class SQLTableAttribute : Attribute
{
public string DatabaseName { get; set; }
public string Schema { get; set; } = "dbo";
public string TableName { get; set; }
}
Thank you!
Alain
My data class is as follows (should be fairly translatable to A above):
public class Foo
{
[Argument(Help = "Name", AssignmentDelimiter = "=")]
public string Name
{
get;
set;
}
}
A helper class is responsible of reading attribute values of objects:
static public string GetCommandLineDelimiter<T>(Expression<Func<T>> property)
{
if(property != null)
{
var memberExpression = (MemberExpression)property.Body;
string propertyName = memberExpression.Member.Name;
PropertyInfo prop = typeof(Arguments).GetProperty(propertyName);
if(prop != null)
{
object[] dbFieldAtts = prop.GetCustomAttributes(typeof(ArgumentAttribute), true);
if(dbFieldAtts.Length > 0)
{
return ((ArgumentAttribute)dbFieldAtts[0]).AssignmentDelimiter;
}
}
}
return null;
}
To use it, simply:
string delimiter = GetCommandLineDelimiter(() => myObject.Name);
That will get the attribute value of AssignmentDelimiter on property Name, i.e. "=".
First, MSDN is your friend.
Then, if you want to get the attributes for ancestors just specify true in the inherit flag of the method:
var attribute = typeof(A).GetProperty("myprop").GetCustomAttributes(true)
.OfType<MycustomAttrib>().FirstOrDefault();
This works. I am doing a lazy initialization of a reference to the custom attribute by using reflection to look at all the properties of all the types.
public class MycustomAttribAttribute : Attribute
{
public MycustomAttribAttribute(string name)
{
this.Name=name;
}
public string Name { get; private set; }
}
class A
{
public A() { MyProp=new B(); }
[MycustomAttrib(name: "OK")]
public B MyProp { get; set; }
}
class B
{
private static Lazy<MycustomAttribAttribute> att = new Lazy<MycustomAttribAttribute>(() =>
{
var types = System.Reflection.Assembly.GetExecutingAssembly().DefinedTypes;
foreach(var item in types)
{
foreach(var prop in item.DeclaredProperties)
{
var attr = prop.GetCustomAttributes(typeof(MycustomAttribAttribute), false);
if(attr.Length>0)
{
return attr[0] as MycustomAttribAttribute;
}
}
}
return null;
});
public string MyProp2
{
get
{
return att.Value.Name;
}
}
}
class Program
{
static void Main(string[] args)
{
// Finds the attribute reference and returns "OK"
string name = (new A()).MyProp.MyProp2;
// Uses the stored attribute reference to return "OK"
string name2 = (new A()).MyProp.MyProp2;
}
}

How to mutate a list of custom objects in GraphQL for .NET

Using GraphQL for .NET, I would like to replace the collection of Foo with a new collection.
Given this server-side code:
public class Foo
{
public Foo(string name)
{
Name = name;
}
public string Name { get; set; }
}
public class Root
{
public Foo[] Foos { get; private set; }
public Foo[] UpdateFoos(Foo[] foos)
{
Foos = foos;
return Foos;
}
}
public class MutationSchema : Schema
{
public MutationSchema()
{
Query = new MutationQuery();
Mutation = new MutationChange();
}
}
public class FooType : ObjectGraphType
{
public FooType()
{
Name = "IndividualFoo";
Field<StringGraphType>("name");
}
}
public class FoosType : ObjectGraphType<ListGraphType<FooType>>
{
public FoosType()
{
Name = "ListOfFoo";
Field<ListGraphType<FooType>>("foos");
}
}
public class FoosInput : InputObjectGraphType
{
public FoosInput()
{
Name = "InputForManyFoo";
Field<ListGraphType<FooInput>>("foos");
Field<ListGraphType<FooType>>("foosResult");
}
}
public class FooInput : InputObjectGraphType
{
public FooInput()
{
Name = "InputForSingleFoo";
Field<StringGraphType>("name");
}
}
public class MutationQuery : ObjectGraphType
{
public MutationQuery()
{
Name = "Query";
Field<FoosType>("queryAllFoos");
}
}
public class MutationChange : ObjectGraphType
{
public MutationChange()
{
Name = "Mutation";
Field<FoosInput>(
"updateAllFoos",
arguments: new QueryArguments(
new QueryArgument<FoosInput>
{
Name = "updateFoosQueryArgument"
}
),
resolve: context =>
{
var root = context.Source as Root;
var change = context.GetArgument<Foo[]>("updateFoosQueryArgument");
// TODO: update collection e.g. return root.UpdateFoos(change);
return change;
}
);
}
}
When I run the mutation query:
mutation M {
fooCollection: updateAllFoos(updateFoosQueryArgument: {
foos: [
{name: "First Foo"},
{name: "Second Foo"}
]}) {
foosResult
}
}
Then I get the following error:
{GraphQL.Validation.ValidationError: Cannot query field "foosResult" on type "InputForManyFoo". Did you mean "foosResult"?}
I'm using the latest version of GraphQL for .NET at the time of writing.
What am I missing?
Working Example: How to mutate a list of custom objects in GraphQL for .NET
My answer from Gitter.
Make an ObjectGraphType for the result. Notice that the “shape” of the object that is returned from resolve matches the “shape” of the graph type.
public class FoosResultType : ObjectGraphType
{
public FoosResultType()
{
Field<ListGraphType<FooType>>("foosResult");
}
}
public class FoosResult
{
public IEnumerable<Foo> FoosResult { get;set; }
}
public class MutationChange : ObjectGraphType
{
public MutationChange()
{
Name = "Mutation";
Field<FoosResultType>(
"updateAllFoos",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<FooInput>>
{
Name = "updateFoosQueryArgument"
}
),
resolve: context =>
{
var root = context.Source as Root;
var change = context.GetArgument<List<Foo>>("updateFoosQueryArgument");
// TODO: update collection e.g. return root.UpdateFoos(change);
return new FoosResult { FoosResult = change };
}
);
}
}
And updated mutation:
mutation M {
fooCollection: updateAllFoos(updateFoosQueryArgument: [
{name: "First Foo"},
{name: "Second Foo"}
]) {
foosResult {
name
}
}
}

How to copy over common base properties using linq

I have 2 different child classes. The main reason is cos the database fields are different from the one going into the Gui. So they both have common parent class.
Class DbChild1 : Parent1
{ ....
}
Class GuiChild1 : Parent1
{....
}
I have a IEnumerable resultset of the DbChild1. I am trying to use Linq to copy the data over. But since there are really lots of properties and I am doing this in more than one place for other similar DBclass and Gui class, I am wondering if there is any shortcut way to copy over the common parent properties instead of copying one by one.
Thanks for all your help in advance.
Cheers
In case you haven't checked AutoMapper you can try this:
class Program
{
static void Main(string[] args)
{
var dbChild1ResultSet = new[]
{
new DbChild1()
{
Field1 = "1",
Field2 = "2",
DbChild1Field1 = "3"
},
new DbChild1()
{
Field1 = "11",
Field2 = "22",
DbChild1Field1 = "33"
},
};
var guiChild1ResultSet = dbChild1ResultSet.Select(x => new GuiChild1(x)
{
GuiChild1Field1 = x.DbChild1Field1
});
}
}
public class Parent1
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public Parent1()
{
}
public Parent1(Parent1 toCopyFrom)
{
Field1 = toCopyFrom.Field1;
Field2 = toCopyFrom.Field2;
}
}
public class DbChild1 : Parent1
{
public string DbChild1Field1 { get; set; }
public DbChild1()
{
}
public DbChild1(Parent1 toCopyFrom) : base(toCopyFrom)
{
}
}
public class GuiChild1 : Parent1
{
public string GuiChild1Field1 { get; set; }
public GuiChild1()
{
}
public GuiChild1(Parent1 toCopyFrom) : base(toCopyFrom)
{
}
}

Categories