.NET - 将列表转换/映射到对象,反之亦然

时间:2017-09-14 08:17:57

标签: c# asp.net-mvc list serialization automapper

我有以下列表,其中包含字段名称和值。

public class FormField 
{
    public string FieldName { get; set;}
    public string FieldValue { get; set;}
}

var formData = new List<FormField>();
formData.Add(new FormField { FieldName = "Date", FieldValue = "2017-09-14" });
formData.Add(new FormField { FieldName = "Name", FieldValue = "Job blogs" });
formData.Add(new FormField { FieldName = "IsEnabled", FieldValue = "true" });

如何将列表转换或映射到以下类?注意FieldNames映射到类的属性。

public class MyViewModel 
{
    [Required]
    public DateTime Date { get; set; } = DateTime.now;

    [Required]
    public string Name { get; set; }

    public boolean IsEnabled { get; set; }

    public IEnumerable<SelectListItem> Titles 
    {
        get
        {
            var options = new List<SelectListItem>
            {
                new SelectListItem(){ Value = "Mr", Text = "Mr" },
                new SelectListItem(){ Value = "Mrs", Text = "Mrs" }                    
            };

            return options;
        }
    } 
}

任何帮助表示赞赏。我需要以某种方式序列化列表吗?可以自动化吗?

*更新*

我尝试了下面的内容,但它不起作用,尽管automapper文档说明你可以直接从字典转到对象:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Dictionary<string, object>, MyViewModel>();
    }
}

var viewModel = Mapper.Map<MyViewModel>(formData.ToDictionary(x => x.FieldName, x => (object) x.FieldValue))

注意:对于记录我使用的是automapper v 5.0.2

我还需要从对象返回到字典,但能够排除public IEnumerable<SelectListItem> Titles {get;}

等属性

3 个答案:

答案 0 :(得分:1)

您似乎正在尝试创建一个包含动态数量字段的对象,例如ViewBag。您不需要为此进行映射。这已经由.NET提供,通过ExpandoObject,DynamicObject类和dynamic关键字提供。

不像构建字段和值列表,而是创建一个ExpandoObject并向其添加字段,就像使用ViewBag一样:

dynamic formData=new ExpandoObject();
formData.Name = "Job blogs";
formData.Date = DateTime.Today;
formData.IsEnabled = true;
formData.Titles = new []{ 
                           new SelectedListeItem{Text="Mr",Value="Mr"},
                           new SelectedListeItem{Text="Mrs",Value="Mrs"}
                        };

您可以将该对象用作ViewModel,就像ViewBag一样。

// Controller 

public ActionResult Index(..)
{
    ....
    View(formData);
}


//View 
@model dynamic 

<h1>@Model.Name</h1>

更新 - 从字段列表中展开

ExpandoObject显式实现IDictionary<string, object>,这意味着可以在运行时不知道其数量或名称的属性,例如:

var fields = new (string name,object value) [] 
             {
                        ("Name","Job blogs"),
                        ("Date", DateTime.Today),
                        ("IsEnabled",true)
             };

dynamic viewModel=new ExpandoObject();
var dict=(IDictionary<string,object>)viewModel;
foreach(var field in fields)
{
    dict.Add(field.name,field.value);
}

我正在使用元组syntaxt来避免重复输入FormField

更新2 - 带字典存储的强类型类

正如Stephen Muecke评论的那样,使用动态类进行绑定和验证更加困难。另一方面,如果事先知道字段,为什么要使用映射或反射?

可以创建一个接受某些文件的ViewModel类,将它们转换为字典(类似于ExpandoObject将要执行的操作),并使用字典作为属性的后备存储。

使用一些像CallMemberName属性一样的C#魔术,额外的代码是最小的。对于字典查找, 是运行时损失,只有在读/写时才会显现

class MyViewModel
{
    Dictionary<string,object> _dict=new Dictionary<string,object>();

    //Get helper
    private T getter<T>([CallerMemberName]string name=null)
    {
        return _dict.TryGetValue(name,out object value)
            ? (T)Convert.ChangeType(value,typeof(T))
            : default(T);            
    }

    private void setter(object value,[CallerMemberName]string name=null)
    {
        _dict[name]=value;
    }

    public DateTime Date { 
        get => getter<DateTime>();            
        set => setter(value); 
    }               
    public string Name { 
        get => getter<string>();            
        set => setter(value); 
    }
    public bool IsEnabled { 
        get => getter<bool>();            
        set => setter(value); 
    }

    public MyViewModel(IEnumerable<FormField> fields)
    {
        _dict=fields.ToDictionary(
                         field=>field.FieldName,
                         field=>(object)field.FieldValue);
    }

}

....

var formData = new [] {
    new FormField { FieldName = "Date", FieldValue = "2017-09-14" },
    new FormField { FieldName = "Name", FieldValue = "Job blogs" },
    new FormField { FieldName = "IsEnabled", FieldValue = "true" }
};

var myViewModel = new MyViewModel(formData);

每个属性的setter只使用属性的名称作为键来设置字典值。 getter使用CallerMemberName获取属性的名称并将其用作键

答案 1 :(得分:0)

如果属性名称匹配,

AM可以默认从IDictionary<string, object>(或DynamicObject)映射到任何目标。所以你需要从你的结构映射到字典,LINQ可能最适合,然后到你的目的地。 docstests

答案 2 :(得分:0)

您可以使用反射来获取和设置模型中的匹配属性

var formData = new List<FormField>();
formData.Add(new FormField { FieldName = "Date", FieldValue = "2017-09-14" });
formData.Add(new FormField { FieldName = "Name", FieldValue = "Job blogs" });
formData.Add(new FormField { FieldName = "IsEnabled", FieldValue = "true" });

// Initialize a new instance of the model
MyViewmodel model = new MyViewmodel();
// Get its property info
PropertyInfo[] properties = model.GetType().GetProperties();
// Loop through your form field data
foreach(var field in formData)
{
    // Get the matching property name
    PropertyInfo pi = properties.FirstOrDefault(x => x.Name == field.FieldName);
    if (pi == null)
    {
        continue;
    }
    // Convert the value to match the property type
    object value = Convert.ChangeType(field.FieldValue, pi.PropertyType);
    // Set the value of the property
    pi.SetValue(model, value);
}