MVC从BeginForm的视图发送集合和引用的对象?

时间:2014-04-23 14:48:59

标签: c# entity-framework asp.net-mvc-4 razor

我的问题是:如何在不编写这么多HiddenFors的情况下传递集合和引用的对象类型?

这是我的模特:

public partial class AddressObject
{
    public int ID { get; set; }
    public string KeyNumber { get; set; }
    public int AddressID { get; set; }
    public int ObjectTypeID { get; set; }
    public double ResidentalArea { get; set; }
    public short ResidentsNumber { get; set; }
    public Nullable<short> TuristBedsNumber { get; set; }

    public virtual Address Address { get; set; }
    public virtual ObjectType ObjectType { get; set; }
    public virtual ICollection<TrashCan> TrashCans { get; set; }
    public virtual ICollection<ObjectOwner> ObjectOwners { get; set; }
}

这是我的观点:

@Html.HiddenFor(model => model.Address.CityID);
@Html.HiddenFor(model => model.Address.Hood);
@Html.HiddenFor(model => model.Address.ID);
@Html.HiddenFor(model => model.Address.Number);
@Html.HiddenFor(model => model.Address.Region);
@Html.HiddenFor(model => model.Address.Street);
@Html.HiddenFor(model => model.Address.City.Addresses);
@Html.HiddenFor(model => model.Address.City.ID);
@Html.HiddenFor(model => model.Address.City.Name);
@Html.HiddenFor(model => model.Address.City.PostalCode);   
@Html.HiddenFor(model => model.AddressID);
@Html.HiddenFor(model => model.ObjectOwners);
@Html.HiddenFor(model => model.ObjectType.ID);
@Html.HiddenFor(model => model.ObjectType.Type);
@Html.HiddenFor(model => model.ObjectTypeID);
@Html.HiddenFor(model => model.TrashCans);
@Html.HiddenFor(model => model.TuristBedsNumber);

我不想为地址写一切。我只想传递地址。我的收藏是ObjectOwners。我想做同样的事情。解决方案存在吗?

编辑:我有Controller及其中的方法ActionResult(AddressObject addressObject)。在该控制器内部,我正在使用存储库调用unitOfWork来保存该实体。我的 HiddenFors包含@HtmlBeginForm("Save", "AddressObject")。当我将模型传递给控制器​​时,我的地址对象AddressObject.Addressnull = Count 0。我希望能够使用我引用的对象和集合传递整个对象,而无需为引用的对象或集合的所有属性编写hiddenfors。

EDIT2: 我有一个主要的细节场景(这没关系),文本框绑定到我的View模型的AddressObject。所以我有@ Html.TextBoxFor(m =&gt; m.AddressID)。问题是,当我更改例如AddressObject.TurisBedsNumber时,每个引用的对象或集合都变为null,因此我的AddressObject在使用我更新的属性从View传递回Controller时不是持久的。我希望我的引用和其他属性不会像在更新之前那样触及。我已经尝试过使用Mvc Futures和序列化整个对象和我的对象及其集合和引用的对象都可以。问题是,当我反序列化我的对象时,该对象没有使用新的TuristBedNumber属性更新;它的旧价值。我想知道如何保存我的集合和其他对象的状态。我可以使用HiddenFor帮助程序保存我的状态(写入的属性太多)或者我可以从我的存储库中获取AddressObject或Collection并在我的控制器中更新它;再次,太多的属性。我想能够说&#34;嘿你收藏和我引用的对象,你无论如何都不会改变&#34;。我想将它们全部序列化,但仅限于它们。 有人要求控制器,但这很常见:

public ActionResult(AddressObject addressObject) { unitOfWork.Update(addressObject) }

是的我有ValidState等....

1 个答案:

答案 0 :(得分:1)

以下是我用于为属性创建隐藏输入的辅助类。如果属性是复杂类型,则递归调用它以为复杂类型的每个属性创建隐藏输入。在您的情况下,您将使用@ Html.HiddenInputFor(m =&gt; m.Address)生成所有17个输入。请务必在web.config中添加命名空间。

using System;
using System.Collections;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using Sandtrap.Web.Extensions;

namespace Sandtrap.Web.Html
{

/// <summary>
/// 
/// </summary>
public static class HiddenInputHelper
{

    /// <summary>
    /// Returns the html for a hidden input(s) of a property.
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="helper"></param>
    /// <param name="expression"></param>
    /// <param name="includeID">
    /// A value indicating the the 'id' attribute should be rendered for the input.
    /// </param>
    /// <remarks>
    /// If the property is a complex type, the methods is called recursively for each property
    /// of the type. Collections and complex types with null value (except those with the 
    /// Required attribute) are ignored.
    /// </remarks>
    public static MvcHtmlString HiddenInputFor<TModel, TValue>
        (this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, bool includeID = false)
    {
        string name = ExpressionHelper.GetExpressionText(expression);
        ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
        StringBuilder html = new StringBuilder();
        return MvcHtmlString.Create(HiddenInput(metaData, name, includeID));
    }

    /// <summary>
    /// Returns the html for a hidden input(s) of a property defined by its metadata.
    /// The string is not html-encoded.
    /// </summary>
    /// <param name="metaData">
    /// The metadata of the property.
    /// </param>
    /// <param name="name">
    /// The name of the property (rendered as the 'name' attribute).
    /// </param>
    /// <param name="includeID">
    /// A value indicating the the 'id' attribute should be rendered for the input.
    /// </param>
    /// <remarks>
    /// If the property is a complex type, the methods is called recursively for each property
    /// of the type. Collections and complex types with null value (except those with the 
    /// Required attribute) are ignored.
    /// </remarks>
    public static string HiddenInputForMetadata(ModelMetadata metaData, string name, bool includeID = false)
    {
        return HiddenInput(metaData, name, includeID);
    }

    #region .Helper methods

    /// <summary>
    /// Returns the html for a hidden input(s) of a property.
    /// </summary>
    /// <param name="metaData">
    /// The property metadata.
    /// </param>
    /// <param name="name">
    /// The name of the property (rendered as the 'name' attribute).
    /// </param>
    /// <param name="includeID">
    /// A value indicating the the 'id' attribute should be rendered for the input.
    /// </param>
    private static string HiddenInput(ModelMetadata metaData, string name, bool includeID)
    {
        StringBuilder html = new StringBuilder();
        if (metaData.ModelType.IsArray && metaData.Model != null)
        {
            // Primarily for database time stamps, this need to called before checking IsComplexType
            // otherwise an endless loop is created
            html.Append(HiddenInput(name, Convert.ToBase64String(metaData.Model as byte[]), includeID));
        }
        else if (metaData.IsComplexType)
        {
            foreach (ModelMetadata property in metaData.Properties)
            {
                if (property.IsCollection() && !property.ModelType.IsArray)
                {
                    // This would just render the Count and Capacity property of List<T>
                    continue;
                }
                if (property.Model == null && property.ModelType != typeof(string) && !property.IsRequired)
                {
                    // Ignore complex types that are null and do not have the RequiredAttribute
                    continue;
                }
                // Recursive call to render a hidden input for the property
                string prefix = string.Format("{0}.{1}", name, property.PropertyName);
                html.Append(HiddenInput(property, prefix, includeID));
            }
        }
        else
        {
            html.Append(HiddenInput(name, metaData.Model, includeID));
        }
        return html.ToString();
    }

    /// <summary>
    /// Returns the html for a hidden input.
    /// </summary>
    /// <param name="name">
    /// The name of the property.
    /// </param>
    /// <param name="value">
    /// The value of the property.
    /// </param>
    /// <param name="includeID">
    /// A value indicating the the 'id' attribute should be rendered for the input.
    /// </param>
    /// <returns></returns>
    private static string HiddenInput(string name, object value, bool includeID)
    {
        TagBuilder input = new TagBuilder("input");
        input.MergeAttribute("type", "hidden");
        if (includeID)
        {
            input.MergeAttribute("id", HtmlHelper.GenerateIdFromName(name));
        }
        input.MergeAttribute("name", name);
        input.MergeAttribute("value", string.Format("{0}", value));
        return input.ToString();
    }

    #endregion

}

}

还需要以下扩展方法

    public static bool IsCollection(this ModelMetadata metaData)
    {
        if (metaData.ModelType == typeof(string))
        {
            return false;
        }
        return typeof(IEnumerable).IsAssignableFrom(metaData.ModelType);
    }