Search code examples
c#system.reflection

C# get properties from result in entitysets wit SystemReflection


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)

Solution

  • 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;
        }