jeudi 9 août 2018

c# Dynamic casting a List

I have some legacy code where I'm trying to condense and remove alot of redundant repetition. I have a List<MessageItemViewModel> as a parameter to a method being called from an ASP.NET MVC POST action. The actual list items will be a subclass of MessageItemViewModel, eg. MessageItemA1ViewModel. In the method being called this list was being cast like so:

switch (level1Code)
            {
                case "A1":
                    {
                        var list = messages.Cast<MessageItemA1ViewModel>().ToList();
                        file = printHelper.Print(list).Save(exportFormat);
                    }
                    break;
                case "A2":
                    {
                        var list = messages.Cast<MessageItemA2ViewModel>().ToList();
                        file = printHelper.Print(list).Save(exportFormat);
                    }
                    break;
                case "A3":

There's about 80 cases in all but I want to reduce it using a dictionary:

private readonly Dictionary<string, Type> _messageItemMap = new 
    Dictionary<string, Type>
    {
        {"A1", typeof(MessageItemA1ViewModel)},
        {"A2", typeof(MessageItemA2ViewModel)},
        {"A3", typeof(MessageItemA3ViewModel)},

and cast the list items to the actual underlying type dynamically. So I've followed examples on SO and the approach works well by declaring a static method and casting against an item in my list:

public static class Ext
{
    public static T Cast<T>(this object entity) where T : class
    {
        return entity as T;
    }
}


public class ExportHelper
{
    public byte[] GetFile(string level1Code, string title, List<MessageItemViewModel> messages, ExportFormat exportFormat)
    {
        .
        .
        .

        var castMethod = typeof(Ext).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(_messageItemMap[level1Code]);
        var anItemType = castMethod.Invoke(null, new object[] { messages[0] });

anItemType is correctly of the actual type I'm casting to. In the immediate window when debugging a call to anItemType is MessageItemA2ViewModel returns true. BUT I need to cast all the items so I tried this:

var castMethod = typeof(Ext).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(_messageItemMap[level1Code]);
var theMessageItemModel = messages
    .Select(p => castMethod.Invoke(null, new object[] { p }))
    .Where(p => p != null)
    .ToList();

This initially seemed to work as hovering over theMessageItemModel debugging showed the values as the type I cast to, but that's just the magic of the Visual Studio tooling. Inspecting items in the immediate window as previously the items are actually defined as System.Object and also I see the return types of the Linq calls return List<Object>. This is an issue later because theMessageItemModel is passed to another library generic method where TypeDescriptor.GetProperties(typeof(T)) is called in order to determine the shape of the POCO to do some further processing. When passed in as a List<Object> 0 properties are found but if I can get the list passed in as one of the subclasses, eg. List<MessageItemA1ViewModel> then it will return properties.

Can anyone suggest how I should use my Cast RuntimeMethodInfo to create a correctly type List of the Type mathing the level1Code in my Type dictionary?





Aucun commentaire:

Enregistrer un commentaire