Casting List Type at Runtime C# Reflection - c#

I've been working on using reflection but its very new to me still. So the line below works. It returns a list of DataBlockOne
var endResult =(List<DataBlockOne>)allData.GetType()
.GetProperty("One")
.GetValue(allData);
But I don't know myType until run time. So my thoughts were the below code to get the type from the object returned and cast that type as a list of DataBlockOne.
List<DataBlockOne> one = new List<DataBlockOne>();
one.Add(new DataBlockOne { id = 1 });
List<DataBlockTwo> two = new List<DataBlockTwo>();
two.Add(new DataBlockTwo { id = 2 });
AllData allData = new AllData
{
One = one,
Two = two
};
var result = allData.GetType().GetProperty("One").GetValue(allData);
Type thisType = result.GetType().GetGenericArguments().Single();
Note I don't know the list type below. I just used DataBlockOne as an example
var endResult =(List<DataBlockOne>)allData.GetType() // this could be List<DataBlockTwo> as well as List<DataBlockOne>
.GetProperty("One")
.GetValue(allData);
I need to cast so I can search the list later (this will error if you don't cast the returned object)
if (endResult.Count > 0)
{
var search = endResult.Where(whereExpression);
}
I'm confusing the class Type and the type used in list. Can someone point me in the right direction to get a type at run time and set that as my type for a list?
Class definition:
public class AllData
{
public List<DataBlockOne> One { get; set; }
public List<DataBlockTwo> Two { get; set; }
}
public class DataBlockOne
{
public int id { get; set; }
}
public class DataBlockTwo
{
public int id { get; set; }
}

You might need something like this:
var endResult = Convert.ChangeType(allData.GetType().GetProperty("One").GetValue(allData), allData.GetType());
Just guessing, didn't work in C# since 2013, please don't shoot :)

You probably want something like this:
static void Main(string[] args)
{
var one = new List<DataBlockBase>();
one.Add(new DataBlockOne { Id = 1, CustomPropertyDataBlockOne = 314 });
var two = new List<DataBlockBase>();
two.Add(new DataBlockTwo { Id = 2, CustomPropertyDatablockTwo = long.MaxValue });
AllData allData = new AllData
{
One = one,
Two = two
};
#region Access Base Class Properties
var result = (DataBlockBase)allData.GetType().GetProperty("One").GetValue(allData);
var oneId = result.Id;
#endregion
#region Switch Into Custom Class Properties
if (result is DataBlockTwo)
{
var thisId = result.Id;
var thisCustomPropertyTwo = ((DataBlockTwo)result).CustomPropertyDatablockTwo;
}
if (result is DataBlockOne)
{
var thisId = result.Id;
var thisCustomPropertyOne = ((DataBlockOne)result).CustomPropertyDataBlockOne;
}
#endregion
Console.Read();
}
public class AllData
{
public List<DataBlockBase> One { get; set; }
public List<DataBlockBase> Two { get; set; }
}
public class DataBlockOne : DataBlockBase
{
public int CustomPropertyDataBlockOne { get; set; }
}
public class DataBlockTwo : DataBlockBase
{
public long CustomPropertyDatablockTwo { get; set; }
}
public abstract class DataBlockBase
{
public int Id { get; set; }
}

Related

Class containing a List of a generically Typed Class

I have a generic class, JSTable, that takes a type RowType. I want to have a class that can contain many JSTables, each with a different RowType, so that I may do something like the Func<> in C# does, where it has many optional types. Is this only possible because there are many representations of Func<>? I want a limitless option, so that I could potentially declare a JSGridVM with hundreds of tables, or one table.
public class JSGridVM<?>//where ? is as many types as I want
{
public List<JSTable<?>> Tables { get; set; };
}
public class JSTable<RowType>
{
public JSTable() { }
public JSTable(List<RowType> rows, List<JSGridColumn> columns, bool allowEditingRows, bool allowDeletingRows, string updateURL, string deleteURL)
{
Rows = rows;
Columns = columns;
AllowEditingRows = allowEditingRows;
AllowDeletingRows = allowDeletingRows;
UpdateURL = updateURL;
DeleteURL = deleteURL;
}
public List<RowType> Rows { get; set; }
public List<JSGridColumn> Columns { get; set; }
public bool AllowEditingRows { get; set; }
public bool AllowDeletingRows { get; set; }
public string UpdateURL { get; set; }
public string DeleteURL { get; set; }
}
public class JSGridColumn
{
public string PropertyName { get; set; }
public ColumnType Type { get; set; }
}
public enum ColumnType
{
Text,
Hidden,
}
Then declare like
var jsGridVM = new JSGridVM<SomeClass1, SomeClass2, SomeClass3>();
OR
var jsGridVM = new JSGridVM<SomeClass1>();
You should declare generic type argument <RowType> not at class level, but at methods: AddTable and GetTable:
public class JSGridVM
{
private Dictionary<Type, object> Tables = new Dictionary<Type, object>();
public JSGridVM AddJSTable<RowType>()
{
Tables.Add(typeof(RowType), new JSTable<RowType>());
return this;
}
public JSTable<RowType> GetJSTable<RowType>()
{
Tables.TryGetValue(typeof(RowType), out object temp);
return (JSTable<RowType>)temp;
}
}
Usage:
var sample = new JSGridVM();
sample.AddJSTable<RowTypeA>().AddJSTable<RowTypeB>();
var test = a.GetJSTable<RowTypeA>();
You could roll your own class, but the .NET DataSet class might be what you are after. DataSet has been around for ages and is still pretty useful.
var ds = new DataSet();
var dataTable = new DataTable("DataTable");
var stringCol = new DataColumn("String Column", typeof(string));
var intCol = new DataColumn("Integer Column", typeof(int));
var decimalCol = new DataColumn("Decimal Column", typeof(decimal));
dataTable.Columns.AddRange(new [] {stringCol, intCol, decimalCol});
var newRow = new object[]
{
"String item",
1,
100.08m
};
dataTable.Rows.Add(newRow);
ds.Tables.Add(dataTable);
var row = ds.Tables["DataTable"].Rows[0];
var stringRowValue = row["String Column"];
var intRowValue = row["Integer Column"];
var decimalRowValue = row["Decimal Column"];
Console.WriteLine($"String value: {stringRowValue}\nInteger value: {intRowValue}\nDecimal Value: {decimalRowValue}");
var rowArr = new DataRow[ds.Tables["DataTable"].Rows.Count];
ds.Tables["DataTable"].Rows.CopyTo(rowArr, 0);
var list = rowArr.ToList();
foreach (DataRow rowitem in list)
{
Console.WriteLine($"\nFrom List: String value: {rowitem["String Column"]}\nInteger value: {rowitem["String Column"]}\nDecimal Value: {rowitem["String Column"]}");
}
Console.ReadKey();
Not sure what you are trying to accomplish, but this is already pretty robust. If you are trying to accomplish a specific use case by creating a generic class, then check out MSDN for more help.
Using Generics:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-type-parameters
Func:
https://msdn.microsoft.com/en-us/library/bb549151(v=vs.110).aspx
Why not :
public class JSGridVM<JSTable>
{
public List<JSTable> Tables { get; set; };
}
public class JSTable<?>
{
}
var rowTypeAnimals = AnimalRowType();
var rowTypeBirds = BirdRowType();
var animalTable = new JSTable<AnimalRowType>(){rowTypeAnimals };
var birdTable = new JSTable<BirdRowType>(){rowTypeBirds };
var grid = new JSGridVM(){animalTable,birdTable};
Keep the row type generic for your table and your grid will always have the same JSTable type.

Get subclass content from object

I have the following classes
public enum Category { foo, foo1, foo2 }
public class Event
{
public DateTime Timestamp { get; set; } = DateTime.Now;
public string GameTime { get; set; }
public string Content { get; set; }
public Person Author { get; set; }
public Category Category { get; set; }
}
and
public class MemberEvent : Event
{
public Member Person { get; set; }
}
The object is created correctly, but if I want to call "Person", this is not displayed to me. If I have a var match, I can call for example match[0].Timestamp but not match[0].Person. The Event object is stored in a List, therefore also the index. I feel I'm missing something simple.
UPDATE: The Code that create the Object
var match = SessionController.Instance.Current;
DataTable dt = dataGrid.ItemsSource as DataTable;
foreach (System.Data.DataRow item in dt.Rows)
{
var memberFoo = new MemberEvent();
memberFoo.Category = Category.Warning;
memberFoo.Time = item["Time"].ToString();
var person = new Person();
person.FirstName = item["FirstName"].ToString();
person.LastName = item["LastName"].ToString();
var passport = new Passport();
passport.Active = true;
passport.PassNumber = item["Pass"].ToString();
passport.Player = person;
memberFoo.Person = passport;
match.Match.Events.Add(memberFoo);
}
SessionController.Instance.Current = match;
Cast your instance to the expected type and test for null to guard for the unexpected:
var memberEvent = match[0] as MemberEvent;
if (memberEvent != null)
{
Console.WriteLine(memberEvent.Person)
}
You will have to cast your Event into a MemberEvent if you want to access the Person property. It is not possible to get the property from Event.
List<Event> myEvents = GetMyEvents();
var myMemberEvent = (MemberEvent)myEvent[0];
It can cause an exception if it can't convert to MemberEvent.

C# Add element to an Object List using variable as List key

Let me explain, I have a model list in which I have a little more than a thousand parameters, so I have to fill the list with some variables, the thing is that I don't want to do this:
list.Add(new Model{
name1= value,
name2= value,
.....
name1000=value
});
I have an array that contains the names of the parameters in the list, so I was wondering if is possible to use that array of the names and in a loop get the variables fill in, something like this:
list.Add(new Model{
//a loop?
array[0]= value
});
Thanks.
You can achieve this using reflection. Code below
public class ModelFactory
{
private IDictionary<string, PropertyInfo> propertiesInfo { get; set; }
public ModelFactory()
{
this.propertiesInfo = typeof(Model)
.GetProperties()
.ToDictionary(p => p.Name, p => p);
}
public Model Create(string[] propertiesToInitialize, dynamic value)
{
var model = new Model();
foreach (var propertyName in propertiesToInitialize)
{
if (this.propertiesInfo.ContainsKey(propertyName))
{
var property = this.propertiesInfo[propertyName];
property.SetValue(model, value);
}
}
return model;
}
}
Model to initialize
public class Model
{
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public int MyProperty4 { get; set; }
public int MyProperty5 { get; set; }
}
Usage
public void Test()
{
var propertiesToInitialize = new string[] { "MyProperty1", "MyProperty2", "MyProperty4" };
var modelFactory = new ModelFactory();
var list = new List<Model>();
list.Add(modelFactory.Create(propertiesToInitialize, 500));
Console.WriteLine("MyProperty1 " + list[0].MyProperty1); // 500
Console.WriteLine("MyProperty2 " + list[0].MyProperty2); // 500
Console.WriteLine("MyProperty3 " + list[0].MyProperty3); // 0
Console.WriteLine("MyProperty4 " + list[0].MyProperty4); // 500
Console.WriteLine("MyProperty5 " + list[0].MyProperty5); // 0
}
However as already mentioned in comments, please reconsider your model design because model with these many properties is not optimal.

How do I create and insert one-to-many object with entity framework c#

I'm trying to create an object and insert to the database but keep getting the same error no matter what I try.
The row that I get the error on is ColumnGroupTest.ValidValues.Add(memberComment1); the error is
error message
NullReferenceException was unhandled by user code
my models
public class StoreColumnName
{
public int Id { get; set; }
public string StoreColumnGroupName { get; set; }
public string ColumnName { get; set; }
public string ColumnType { get; set; }
public List<StoreValidValue> ValidValues { get; set; }
}
public class StoreValidValue
{
public int Id { get; set; }
public string ValidValue { get; set; }
public StoreColumnName StoreColumnName { get; set; }
}
my controller
public ActionResult Index()
{
XDocument document = XDocument.Load(#"C:\Users\Physical.xml");
var result = document.Descendants("ColumnGroup");
foreach(var item in result){
var ColumnGroupName = item.Attribute("name").Value;
var Columns = item.Descendants("Column");
foreach (var itemColumn in Columns)
{
StoreColumnName ColumnGroup = new StoreColumnName();
var ColumnGroupTest = new StoreColumnName
{
StoreColumnGroupName = ColumnGroupName,
ColumnName = itemColumn.Attribute("name").Value,
ColumnType = itemColumn.Attribute("type").Value,
Id = 11
};
var ValidValues = itemColumn.Descendants("ValidValues");
var Values = ValidValues.Descendants("Value");
foreach (var Value in Values)
{
var memberComment1 = new StoreValidValue
{
StoreColumnName = ColumnGroupTest,
ValidValue = Value.Value,
Id = 101
};
ColumnGroupTest.ValidValues.Add(memberComment1);
}
}
}
return View();
}
(I gladly take tips on what I can improve when asking for help/guiding here).
Can anyone help ?
The issue that you're having is that you don't initialize your ValidValues property to a list. By default, those types of properties initialize to null unless you specify differently.
The best approach is to add that initialization to your constructor of that object.
public StoreColumnName() {
this.ValidValues = new List<StoreValidValue>();
}

Get values from a HashSet<T> using reflection

I need a way to get the values from a generic HashSet using reflection. Here is what I've tried (you can copy/paste this on a console app):
class Program
{
public class Order
{
public int Id { get; set; }
}
public class Person
{
public string Name { get; set; }
public ICollection<Order> Orders { get; set; }
}
static void Main(string[] args)
{
var person = new Person();
person.Name = "Test Person";
person.Orders = new HashSet<Order>();
person.Orders.Add(new Order() { Id = 1 });
person.Orders.Add(new Order() { Id = 2 });
var reflectedOrders = person.GetType().GetProperty("Orders").GetValue(person, null);
Console.WriteLine("How do I iterate the reflected orders?");
Console.ReadLine();
}
}
EDIT
It's an example, in the real application I don't know which type to convert the reflected Orders. I only know the property is an ICollection<T> (turned to HashShet by EF)
Did you tried casting reflectedOrders to IEnumerable?
IEnumerable reflectedOrders = (IEnumerable)person.GetType().GetProperty("Orders").GetValue(person, null);
It should be simple as casting:
var reflectedOrders = (HashSet<Order>) person.GetType().GetProperty("Orders").GetValue(person, null);
foreach (var order in reflectedOrders)
...
What about
var orders = persons.OfType<Person>().SelectMany(p => p.Orders);

Categories