You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

控制器重复调用ViewModel时,其构造函数会清空其他值吗?

问题描述

我曾尝试搜索该问题的答案,但只找到向Controller传递数据相关的内容,并非我所问的问题。如果Stack Overflow上已有相关答案,请告知我。

  • 我们的ViewModel包含一个InventoryIn类实例作为属性。
  • 我们希望重复调用同一视图,将ViewModel传回该视图。我们在视图中填充了InventoryIn的数据,但第二次调用时,InventoryIn实例变为空。
  • 我能想到的唯一解释是InventoryIn的构造函数(我们在其中初始化了一些属性)。因此我的问题是:在Controller中重复向同一视图传递ViewModel时,ViewModel的构造函数是否总会被调用?我们原本认为不会,但可能有误。

另外,视图结构包含两个<form>标签:一个按钮提交到同一视图,调用视图首次加载时使用的同一Action方法;另一个<form>标签的Action方法提交到同一Controller下的另一个视图。


补充代码:控制器Action方法

public IActionResult Step1()
{
    var newSiteToSite = new SiteToSite();
    //do stuff here with newSiteToSite
    return View(newSiteToSite);
}

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Step2(SiteToSite SiteToSite)
{
    // THIS COULD BE COMING FROM STEP 1 OR STEP 2
    if (Step1IsValid(SiteToSite))
    {
        RefreshSiteToSite(SiteToSite);
        // RETURN STEP 2 VIEW
        return View("Step2", SiteToSite);
    // STEP 1 IS INVALID
    } else {
        // RETURN STEP 1 VIEW
        return View("Step1", SiteToSite);
    }
}

RefreshSiteToSite方法

private void RefreshSiteToSite(SiteToSite siteToSite)
{
    SetOriginatingLocation(siteToSite);

    SetDestinationLocation(siteToSite);

    int invOutItemPackageReceiptId = siteToSite.InvOutItemPackageReceiptID;
    GetSiteToSiteById(siteToSite, invOutItemPackageReceiptId);
}

SetOriginatingLocation方法

private void SetOriginatingLocation(SiteToSite siteToSite)
{
    var originatingLocation = db.PharmLocations.Where(p => p.LocationId == siteToSite.OriginatingLocationID).FirstOrDefault();
    if (originatingLocation != null)
    {
        siteToSite.OriginatingLocationCode = originatingLocation.LocationCode;
        siteToSite.OriginatingLocationName = originatingLocation.Location;
        siteToSite.OriginatingLocationAddress1 = originatingLocation.Address1;
        siteToSite.OriginatingLocationAddress2 = originatingLocation.Address2;
        siteToSite.OriginatingLocationLocCity = originatingLocation.LocCity;
        siteToSite.OriginatingLocationLocState = originatingLocation.LocState;
        siteToSite.OriginatingLocationLocZip = originatingLocation.LocZip;
        siteToSite.OriginatingLocationPhoneMain = originatingLocation.PhoneMain;
        siteToSite.OriginatingLocationPhoneAlt = originatingLocation.PhoneAlt;
        siteToSite.OriginatingLocationFax = originatingLocation?.Fax;
        siteToSite.OriginatingLocationContact1Name = originatingLocation?.Contact1Name;
        siteToSite.OriginatingLocationContact2Name = originatingLocation?.Contact2Name;
    }
}

SetDestinationLocation方法

private void SetDestinationLocation(SiteToSite siteToSite)
{
    var destinationLocation = db.PharmLocations.Where(p => p.LocationId == siteToSite.DestinationLocationID).FirstOrDefault();
    if (destinationLocation != null)
    {
        siteToSite.DestinationLocationCode = destinationLocation.LocationCode;
        siteToSite.DestinationLocationName = destinationLocation.Location;
        siteToSite.DestinationLocationAddress1 = destinationLocation.Address1;
        siteToSite.DestinationLocationAddress2 = destinationLocation.Address2;
        siteToSite.DestinationLocationLocCity = destinationLocation.LocCity;
        siteToSite.DestinationLocationLocState = destinationLocation.LocState;
        siteToSite.DestinationLocationLocZip = destinationLocation.LocZip;
        siteToSite.DestinationLocationPhoneMain = destinationLocation.PhoneMain;
        siteToSite.DestinationLocationPhoneAlt = destinationLocation.PhoneAlt;
        siteToSite.DestinationLocationFax = destinationLocation?.Fax;
        siteToSite.DestinationLocationContact1Name = destinationLocation?.Contact1Name;
        siteToSite.DestinationLocationContact2Name = destinationLocation?.Contact2Name;
    }
}

GetSiteToSiteById方法

private void GetSiteToSiteById(SiteToSite siteToSite, int id)
{
    var tmp = db.VwPart1SiteToSites.Where(p => p.InvOutItemPackageReceiptId == id).FirstOrDefault();
    if (tmp != null)
    {
        if (siteToSite.CurrentVwPart1s == null)
        {
            siteToSite.CurrentVwPart1s = new List<VwPart1SiteToSite>();
        }
        siteToSite.CurrentVwPart1s.Add(tmp);
    }
}

解答

关于ViewModel构造函数调用的问题

你当前的代码逻辑里,ViewModel的构造函数不会在Post Action返回视图时被重新调用——因为你返回的是已经通过模型绑定创建好的SiteToSite实例(也就是Action参数里的对象),而非重新new一个新实例。

InventoryIn实例为空的原因

问题核心出在模型绑定机制:表单提交时,只有表单中存在的字段会被绑定到ViewModel属性上。如果InventoryIn的属性没有在视图中生成对应的输入控件(如<input>、<select>),或者控件命名不匹配,模型绑定器无法填充这些值,最终导致InventoryIn实例为空或属性值丢失。

解决建议

  1. 通过隐藏字段保留数据:若需要回传InventoryIn的数据,在视图中为其每个需保留的属性添加隐藏字段(如@Html.HiddenFor(m => m.InventoryIn.PropertyName)),确保表单提交时这些值能被带回Controller。
  2. 重新加载数据:如果InventoryIn的数据可从数据库或其他数据源获取,在Post Action中根据ViewModel的标识(如ID)重新查询并填充InventoryIn实例,类似你在RefreshSiteToSite中处理地址信息的逻辑。
  3. 检查控件命名匹配:确保视图中输入控件的名称与ViewModel属性名称完全一致(如InventoryIn.PropertyName),模型绑定器依赖名称匹配完成赋值。

额外注意

视图中的两个<form>标签,需确保每个表单包含当前步骤回传所需的所有必要数据。尤其是从Step2返回Step1时,Step1依赖的所有字段要么在表单中存在,要么在Action中重新加载,否则ViewModel对应属性会丢失数据。

内容的提问来源于stack exchange,提问作者Rod

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:27:28