ASP MVC.NET - как связать KeyValuePair?

Можно ли связать такой вид свойства?

public KeyValuePair<string, string> Stuff { get; set; }

Я попытался использовать следующий код в представлении, но он не работает:

<%=Html.Text("Stuff", Model.Stuff.Value)%>    
<%=Html.Hidden("Model.Stuff.Key", Model.Stuff.Key)%>

Ответ 1

KeyValuePair<K,V> - это структура, а не класс, поэтому каждый вызов вашего свойства Stuff возвращает копию оригинала KeyValuePair. Итак, когда вы привязываетесь к Model.Stuff.Value и Model.Stuff.Key, вы фактически работаете над двумя разными экземплярами KeyValuePair<K,V>, ни один из которых не является одним из вашей модели. Поэтому, когда они обновляются, он не обновляет свойство Stuff в вашей модели... QED

Кстати, свойства Key и Value доступны только для чтения, поэтому вы не можете их изменять: вам нужно заменить экземпляр KeyValuePair

Следующее обходное решение должно работать:

Модель:

private KeyValuePair<string, string> _stuff;
public KeyValuePair<string, string> Stuff
{
    get { return _stuff; }
    set { _stuff = value; }
}

public string StuffKey
{
    get { return _stuff.Key; }
    set { _stuff = new KeyValuePair<string, string>(value, _stuff.Value); }
}

public string StuffValue
{
    get { return _stuff.Value; }
    set { _stuff = new KeyValuePair<string, string>(_stuff.Key, value); }
}

Вид:

<%=Html.Text("Stuff", Model.StuffValue)%>    
<%=Html.Hidden("Model.StuffKey", Model.StuffKey)%>

Ответ 2

<%=Html.Text("Stuff.Value", Model.Stuff.Value)%>

Может ли работать?

Ответ 3

Если вам нужно связать словарь, чтобы каждое значение имело текстовый редактор для редактирования, ниже это один из способов заставить его работать. Очень важными частями, которые влияют на то, как генерируется атрибут name в HTML, является выражение модели, что и обеспечивает привязку модели при обратной передаче. Этот пример работает только для словаря.

Связанная статья объясняет синтаксис HTML, который делает работу привязки, но он оставляет синтаксис Razor, чтобы выполнить эту довольно загадочную задачу. Кроме того, статья совершенно отличается тем, что они позволяют редактировать как ключи, так и значения, и используют целочисленный индекс, даже если ключ словаря - это строка, а не целое число. Поэтому, если вы пытаетесь связать словарь, вам действительно нужно сначала оценить, хотите ли вы, чтобы значения были доступны для редактирования или оба ключа и значения, прежде чем вы решите, какой подход принять, потому что эти сценарии совершенно разные.

Если вам когда-либо понадобится привязка к сложному объекту, то есть к словарю, тогда вы должны просто иметь текстовое поле для каждого свойства с выражением, сверлящим свойство, подобно статье.

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

 public class SomeVM
    {
        public Dictionary<string, string> Fields { get; set; }
    }

    public class HomeController : Controller
    {
        [HttpGet]
        public ViewResult Edit()
        {
            SomeVM vm = new SomeVM
            {
             Fields = new Dictionary<string, string>() {
                    { "Name1", "Value1"},
                    { "Name2", "Value2"}
                }
            };

            return View(vm);

        }

        [HttpPost]
        public ViewResult Edit(SomeVM vm) //Posted values in vm.Fields
        {
            return View();
        }
    }

CSHTML:

Редакторы только для значений (конечно, вы можете добавить LabelFor для создания меток на основе ключа):

@model MvcApplication2.Controllers.SomeVM

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>SomeVM</legend>

        @foreach(var kvpair in Model.Fields)
        {
            @Html.EditorFor(m => m.Fields[kvpair.Key])  //html: <input name="Fields[Name1]" …this is how the model binder knows during the post that this textbox value gets stuffed in a dictionary named "Fields", either a parameter named Fields or a property of a parameter(in this example vm.Fields).
        }

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

Редактирование обоих ключей/значений:       @{var fields = Model.Fields.ToList(); }

    @for (int i = 0; i < fields.Count; ++i) 
    {
        //It is important that the variable is named fields, to match the property name in the Post method viewmodel.
        @Html.TextBoxFor(m => fields[i].Key)
        @Html.TextBoxFor(m => fields[i].Value)

        //generates using integers, even though the dictionary doesn't use integer keys,
        //it allows model binder to correlate the textbox for the key with the value textbox:            
        //<input name="fields[0].Key" ...
        //<input name="fields[0].Value" ...

        //You could even use javascript to allow user to add additional pairs on the fly, so long as the [0] index is incremented properly
    }

Ответ 4

Я знаю, что это немного более старый вопрос, но мне не понравилось какое-либо предлагаемое решение, поэтому я даю свое. Я переписал стандартную привязку модели для обработки KeyValuePairs, поэтому я могу использовать их по-прежнему.

public class CustomModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var model = base.BindModel(controllerContext, bindingContext);            
        model = ResolveKeyValuePairs(bindingContext, model);
        return model;
    }

    private object ResolveKeyValuePairs(ModelBindingContext bindingContext, object model)
    {
        var type = bindingContext.ModelType;
        if (type.IsGenericType)
        {
            if (type.GetGenericTypeDefinition() == typeof (KeyValuePair<,>))
            {                    
                var values = bindingContext.ValueProvider as ValueProviderCollection;
                if (values != null)
                {
                    var key = values.GetValue(bindingContext.ModelName + ".Key");
                    var keyValue = Convert.ChangeType(key.AttemptedValue, bindingContext.ModelType.GetGenericArguments()[0]);
                    var value = values.GetValue(bindingContext.ModelName + ".Value");
                    var valueValue = Convert.ChangeType(value.AttemptedValue, bindingContext.ModelType.GetGenericArguments()[1]);
                    return Activator.CreateInstance(bindingContext.ModelType, new[] {keyValue, valueValue});
                }

            }
        }
        return model;
    }