vendredi 20 janvier 2017

using a Type variable in generic collection

I have a WebAPI method and in it I have a an IEnumerable of objects where the type of T is not known until runtime. So, I need to execute a foreach on the IEnumerable collection. The problem is I cannot replace the T with a specific type since that type is not known until runtime. So, I get the type of T using some reflection in a helper method and store it into a variable "type". But apparently, I cannot use IEnumerable. It throws an error saying a variable cannot be used as a type.

My WebAPI method needs to log activity whenever an update happens to certain types of data. So, I have a custom ActionFilter attribute setup to do the dirty work.

Here is my WebAPI method (this is just a dummy test method):

    [HttpPost]
    [Route("testlogging")]
    [PhiLog]
    public IEnumerable<Person> UpdateTestData(Person person)
    {
        IList<Person> persons = new List<Person>();
        persons.Add( person );
        persons.Add( person );
        return persons;
    }

When OnActionExecuted is executed, we don't know the type of response content returned to the caller. It could be just null, or just a string, or just an int or IList or IEnumerable and the type of T is not known. Here is my Custom Attribute:

public class PhiLogAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted( HttpActionExecutedContext actionExecutedContext )
    {
        var user = AuthenticationHelper.GetCurrentUserFromRequest( actionExecutedContext.Request );
        var actionDescriptor = actionExecutedContext.Request.GetActionDescriptor();
        var controllerName = actionDescriptor.ControllerDescriptor.ControllerName;
        var methodName = actionDescriptor.ActionName;
        var action = string.Format( "{0}/{1}", controllerName, methodName );

        var responsePayload = actionExecutedContext.Response.Content as ObjectContent;
        var payloadType = responsePayload.ObjectType;

        /* The variable below (payloadObj) could be of any type - an int, a string or a generic ICollection.  The code below is just assuming it is of type IList<Person>.  This is for my test purpose */
        var payloadObj = responsePayload.Value;

        /* AppHelper.GetEnumerableType returns the type of the generic parameter T.
            In this case, a Person type
        */
        var type = AppHelper.GetEnumerableType( payloadObj.GetType());

        if ( payloadType.GetInterfaces().Any( x => x.GetType().Name == "ICollection`1") )
        {
            /* This is where I am stumped.  I need to walk this ICollection<T> and log some value from few properties of T.  At runtime, payloadObj is of type object.  I need to cast it into the correct ICollection type */
            foreach (var x in (ICollection<type>)payloadObj)
            {
                //do something with var x here.

                /* But ICollection<type> throws an error "type is a variable but used like a type" */
            }
        }
    }
}

Here is my helper method to get the type of the type parameter T.

public static Type GetEnumerableType( Type type )
{
    var interfaceTypes = type.GetInterfaces();

    foreach ( Type interfaceType in interfaceTypes )
    {
        if ( interfaceType.IsGenericType && interfaceType.Name == "ICollection`1" )
            return interfaceType.GetGenericArguments()[ 0 ];
    }
    return null;
}

I have commented inline in the code where the problem is. Anyone please tell me how can I use a variable in place of T in an IEnumerable. Thanks.





Aucun commentaire:

Enregistrer un commentaire