简体   繁体   中英

How to combine many properties into one List<string> property in C#

I am a little bit confused about how to read data from Excel. I am trying to import Excel for updating product list, I create an Excel model; I added all basic properties like name, price, quantity, etc. into this model. I will read all Excel and map into this model. That's ok, then I will give this model to EF Core 5 to save to SQL Server.

public class ExcelModel
{ 
    public string Name { get; set }
    public int Price { get; set }
    public int Quantity { get; set }
}

I have a problem with product options. According to my DB schema, I have one table of products, one for options, one for option values, one for productOptionRelation.

在此处输入图像描述

Can you suggest another solution way or just solve on my way?

My colleges did this created field corresponding to values. like option1 and optionValue1, option2 and optionValue2 many of them, because each product could have many options. Model look like that, 20 option and 20 value was declared here and they manually map all these

坏模型的图片

For a temporary solution, I limited this option up to 5 and I created an list. and encapsulate all of them into list

public class ExcelOptionViewModel
{
    public string Option { get; set; }
    public string Value { get; set; }
}

This is my temp model, I encapsulated like that.

 public IList<ExcelOptionViewModel> OptionModels { get; set; } = new List<ExcelOptionViewModel>(); 

 public string Option1
 {
     get { return OptionModels[0].Option; } 
     set
     {
         this.OptionModels.Insert(0, new ExcelOptionViewModel { Option = value });
     }
 }

 public string Option1Value
 {
     get { return OptionModels[0].Value; }
     set { this.OptionModels[0].Value  = value; }
 }

This would be unlimited, You should enter how much you want

I have 2 solutions still I am researching one is, creating a method inside the excelviewmodel, this method will add all options and values into a list or I will use reflection, I am looking something like underlying type I will all option and values this underlying base type or something, when property loop came here, checking the type and assign all option1,option2,option3 or name like that properties to List<string> options , and same for the option values. I will use reading like option[0] and optionvalue[0]

Excel column names must be different because I read excel and turn it into datatable. Datatable column names must be different, it's not valid for reading into datatable

excel导入错误的样本

excel导入真实样本

I used basically excel to data table function I can't remember but probably I found it in StackOverflow. Also, I added a feature there If some cell is null it will miss.

public List<T> ConvertDataTableToList<T>(DataTable dt)
{
    //datatable clomun names
    var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList();

    //selection properties equals to columnnames because I dont want loop for all props
    var properties = typeof(T).GetProperties().Where(prp => columnNames.Any(t => t.ToLower() == prp.Name.ToLower()));

    return dt.AsEnumerable().Select(row =>
    {
        var objT = Activator.CreateInstance<T>();
        foreach (var pro in properties)
        {
            try
            {
                if (row[pro.Name] != DBNull.Value)
                    pro.SetValue(objT, row[pro.Name], null);
                else
                    pro.SetValue(objT, null, null);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        return objT;
    }).ToList();
}

I am looking something here when option1 or option2 comes here it would put this into a list

Also in my dt to model converter I dont want to use If but if some data value is null It throws an error which cant convert from dbnull value. If you have a suggest for it I would like release if condition:)

When All done I will map this excelviewmodel to product model something like this

 foreach (var prop in SideParams.columns)
            {
                var source = row.GetType().GetProperty(prop);

                var destination = product.GetType().GetProperty(prop);

                if (destination != null && source.GetValue(row) != null)
                {
                Type t = Nullable.GetUnderlyingType(destination.PropertyType) ?? destination.PropertyType;

                object safeValue = Convert.ChangeType(source.GetValue(row), t);
                destination.SetValue(product, safeValue);

            }
        }

I saw something here https://docs.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-6.0

it about binding flangs when reflecting model. "Specifies flags that control binding and the way in which the search for members and types is conducted by reflection." If there is way I can redirect option(1-2-3-4-5-6...) to list options

thanks for the help I solved my problem. If you need something like that, my solution is;

As you know OptionModels is what I created before, AddOptipns function is a new one I use for add data to list, The function work with the ref, otherwise it must be static, if I turn it static, option models also must be static, so I can't access the list.

 public IList<ExcelOptionViewModel> OptionModels { get; set; } = new List<ExcelOptionViewModel>();
 public void AddOptions(ref String option, ref String value)
     {
        OptionModels.Add(new ExcelOptionViewModel { Option = option.Trim(), Value = value.Trim() });
     }

And also add some new parts to convert model function,

calling that AddOptions method with reflection, I got an example from here https://docs.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-6.0

I was inspired by the swap example there.

public List<T> ConvertDataTableToList<T>(DataTable dt)
    {
        var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList();

        //selection properties equals to columnnames because I dont want loop for all props
        var type = typeof(T);
        var properties = type.GetProperties().Where(prp => columnNames.Any(t => t.ToLower() == prp.Name.ToLower())).ToList();

        var productOptions = columnNames.Where(x => x.Contains("option")).ToList() ?? new List<string>();

        return dt.AsEnumerable().Select(row =>
        {
            var objT = Activator.CreateInstance<T>();
            foreach (var pro in properties)
            {
                try
                {
                    if (row[pro.Name] != DBNull.Value)
                        pro.SetValue(objT, row[pro.Name], null);
                    else
                        pro.SetValue(objT, null, null);
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }

            for (var i = 0; i < productOptions.Count(); i += 2)
            {
                object[] argValues = new object[] { row[productOptions[i]].ToString(), row[productOptions[i + 1]].ToString() };
                String[] argNames = new String[] { "option", "value" } ;
                 
               var method =  type.GetMethod("AddOptions");
                method.Invoke(objT, argValues);
            }

            return objT;
        }).ToList();
    }

here is the added data:)

添加数据的示例

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM