So I have a want to write a piece of code that get's the result out of an EntitySet and puts the data in another object by utilizing SystemReflection.
Example
Object Ticket
has a property called Application
, this property's value has been defined in a relation table called TicketRelation
. The program has to check for the Application
property in TicketRelation
and if it's there it will pass the data into the Application
property of Ticket
.
This is what I have so far:
private object ConvertRelations(object origin, object to)
{
// haal relTable op met getValue op relatie property.
// The propertyInfo types are EntitySets
List<PropertyInfo> relationProperties = new List<PropertyInfo>();
PropertyInfo[] cRels = to.GetType().GetProperties();
foreach (var property in origin.GetType().GetProperties())
{
// all the possible endings of relation table names
if (property.Name.EndsWith("Relatie") || property.Name.EndsWith("Rel") || property.Name.EndsWith("Relaties"))
{
relationProperties.Add(property);
}
}
foreach (var relProp in relationProperties)
{
var parent = relProp.GetValue(origin, null);
var parentProps = parent.GetType().GetProperties();
object match;
PropertyInfo[] matchProps = null;
foreach(var parentProp in parentProps)
{
// the property's name of which I assumed to hold the data I want was called Item
if (parentProp.Name == "Item")
{
match = parentProp.GetValue(parent, null);
if(match != null)
{
matchProps = match.GetType().GetProperties();
}
}
}
}
// this will return the result later on.
return null;
}
But somehow the GetValue method I try to invoke on parentProp doesn't work. I use this kind of method everytime for GetValue, but when I do it with an EntitySet
it throws an exception which I don't understand.
The stack trace it throws at parentProp.GetValue(parent, null)
:
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
at TicketSystemDb.DTO.Helper.DTOHelper.ConvertRelations(Object origin, Object to) in C:\TicketSystem\TicketSystemDb\DTO\Helper\DTOHelper.cs:line 90
at TicketSystemDb.DTO.Helper.DTOHelper.SimplifyResult(Object from, Object to) in C:\TicketSystem\TicketSystemDb\DTO\Helper\DTOHelper.cs:line 61
at TicketSystemDb.REPO.TicketREPO.GetTickets() in C:\TicketSystem\TicketSystemDb\REPO\TicketREPO.cs:line 35
at TicketSystemDb.UOW.TicketUOW.GetTickets() in C:\TicketSystem\TicketSystemDb\UOW\TicketUOW.cs:line 21
at TicketSystemAPI.Controllers.TicketController.Get() in C:\TicketSystem\TicketSystemAPI\Controllers\TicketController.cs:line 22
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_1.<GetExecutor>b__3(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
I finally got the solution after an hour of testing. I just simply redesigned the database so that there was only one relation table per ticket. This made it possible for me to get the data from the relation table. the main problem here was that I could not get the result from the relationtables because there were multiple, and I couldn't access any of the results without using LINQ bound to a specific object. This is the easier solution but I wanted to make it more dynamic and global.
So with that in mind I just had to link the Ticket table to the corresponding relation table and not the ohter way around. After doing this I also made some changes in my code like this:
private object ConvertRelations(object origin, object to)
{
List<PropertyInfo> relationProperties = new List<PropertyInfo>();
PropertyInfo[] cRels = to.GetType().GetProperties();
foreach (var property in origin.GetType().GetProperties())
{
if (property.Name.EndsWith("Relatie") || property.Name.EndsWith("Rel") || property.Name.EndsWith("Relaties"))
{
// initial check for class to filter out the auto-generated integer properties
if(property.PropertyType.IsClass)
relationProperties.Add(property);
}
}
// to prevent unnecessary work
if(relationProperties.Count == 0)
{
return null;
}
foreach (var relProp in relationProperties)
{
var parent = relProp.GetValue(origin, null);
var parentProps = parent.GetType().GetProperties();
object match;
// removed matchProps because the were not needed anymore
// loop through cRels because of the database redesign
foreach(var rel in cRels)
{
// This code has been changed because the relation property wasn't an EntitySet anymore.
if (rel.PropertyType.IsClass)
{
// get the link through LINQ instead
var link = parentProps.SingleOrDefault(pr => pr.Name == rel.Name);
if (link != null)
{
// this speaks mostly for itself
// the simplifyresult is used to convert the match to the DTO model to match the property, which is a DTO
match = link.GetValue(parent, null);
rel.SetValue(to, SimplifyResult(match, Assembly.GetExecutingAssembly().CreateInstance("TicketSystemDb.DTO."+rel.PropertyType.Name)), null);
}
}
}
}
return to;
}