Search code examples
c#objecttypescastingtypeof

Convert List<object> to List<model> type with only the model name c# mvc


So I'm trying to create a generic function (currently it's not but I wanted to future proof it, so in a later stage the tableName can be picked/passed through).

I want to use the string Customers_Model to get the model and for the variable c to be of type Customers_Model but I keep getting type object.

I know that you can do: (Customers_Model)Activator.CreateInstance(type) but that assumes that you know that model type but I've only got the model type from a string. I feel as if there's something to do with the type variable as it contains the model name but can't quite put my finger on it.

I've also seen this but it assumes that you know of a function that you want to access within your model. In this case we don't know any functions/fields within there to use this solution only the modelName.

        public async Task<ActionResult> Index()
        {
            var tableName = "Customers";
            string modelName = "MVC.Models." + tableName + "_Model";
            Type type = Type.GetType(modelName);
            var d = Activator.CreateInstance(type);
            var c = Convert.ChangeType(d, type);
            var data = await DBServices.GetFromTable(tableName, c);
            if (data.Count > 0)
            {
                return View(data);
            }
            else 
                return View();
        }

EDIT 1

I would like for the user to pick a table and then have it display the table in the View. My View accepts a model GeneralViewModel

GeneralViewModel:

    public class GeneralViewModel
    {
        public List<Customers_Model> Customers {get; set;}
    }

At a later stage I would like to be able to add in more models to this and not have to change the controller/View as it's been generalised.

Should I instead convert c to a GeneralViewModel instead and when it's displaying in the view then to just look for the first List where the count > 0 and to diplay that? I only expect one list in GeneralViewModel to be populated at a time.

EDIT 2

Here's a couple of functions from my DBServices because that the T type being sent in is object (due to var c above) then the return type is object aswell. If I can just sort out the type being passed in then this will all be resolved.

        public static async Task<List<T>> GetFromTable<T>(string tableName, T type)
        {
            var results = new List<T>();
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                string cmdText = "SELECT * FROM " + tableName + ";";

                SqlCommand cmd = new SqlCommand(cmdText, conn);
                try
                {
                    conn.Open();
                    SqlDataReader reader = await cmd.ExecuteReaderAsync();

                    while (reader.Read())
                    {
                        if (reader != null)
                        {
                            results.Add(ConvertReader((IDataReader)reader, type));
                        }
                    }
                    reader.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            return results;
        }

        public static T ConvertReader<T>(IDataReader reader, T value)
        {
            //var item = value;
            var typeProperties = value.GetType().GetProperties();

            foreach (var property in typeProperties)
            {
                DisplayNameAttribute attr = (DisplayNameAttribute)Attribute.GetCustomAttribute(property, typeof(DisplayNameAttribute));

                int ordinal;
                if (attr != null)
                {
                    ordinal = reader.GetOrdinal(attr.DisplayName);
                }
                else
                {
                    ordinal = reader.GetOrdinal(property.Name);
                }

                if (!reader.IsDBNull(ordinal))
                {
                    property.SetValue(value, reader[ordinal], null);
                }
            }

            return value;
        }

Solution

  • I ended up just creating a Dictionary<string, dynamic> which I used to convert the string to the model type. I then passed that into the GetFromTable function as the second parameter. It then returned the right results.