加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc-3 – 发布表单时控件中的模型绑定 – 不会自动加载

发布时间:2020-12-15 22:31:23 所属栏目:asp.Net 来源:网络整理
导读:我正在使用Entity Framework 4.2版.我的小测试应用程序中有两个类: public class TestParent{ public int TestParentID { get; set; } public string Name { get; set; } public string Comment { get; set; } public virtual ICollectionTestChild TestChi
我正在使用Entity Framework 4.2版.我的小测试应用程序中有两个类:
public class TestParent
{
    public int TestParentID { get; set; }
    public string Name { get; set; }
    public string Comment { get; set; }

    public virtual ICollection<TestChild> TestChildren { get; set; }
}

public class TestChild
{
    public int TestChildID { get; set; }
    public int TestParentID { get; set; }
    public string Name { get; set; }
    public string Comment { get; set; }

    public virtual TestParent TestParent { get; set; }
}

使用数据库中的数据填充对象效果很好.所以我可以在我的代码中使用testParent.TestChildren.OrderBy(tc => tc.Name).First().Name等.

然后我为testParents构建了一个标准的编辑表单.控制器看起来像这样:

public class TestController : Controller
{
    private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
    private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();

    public ActionResult ListParents()
    {
        return View(testParentRepository.TestParents);
    }

    public ViewResult EditParent(int testParentID)
    {
        return View(testParentRepository.TestParents.First(tp => tp.TestParentID == testParentID));
    }

    [HttpPost]
    public ActionResult EditParent(TestParent testParent)
    {
        if (ModelState.IsValid)
        {
            testParentRepository.SaveTestParent(testParent);
            TempData["message"] = string.Format("Changes to test parents have been saved: {0} (ID = {1})",testParent.Name,testParent.TestParentID);
            return RedirectToAction("ListParents");
        }
        // something wrong with the data values
        return View(testParent);
    }
}

当表单回发到服务器时,模型绑定似乎运行良好 – 即testParent看起来没问题(id,name和comment按预期设置).但是导航属性TestChildren保持为NULL.

我猜这并不令人惊讶,因为模型绑定只是提取表单值,因为它们是从浏览器发送的,并将它们推送到TestParent类的对象中.然而,填充testParent.TestChildren需要立即往返数据库,这是实体框架的责任. EF可能不参与绑定过程.

然而,当我调用testParent.TestChildren.First()时,我期待延迟加载.相反,它会导致ArgumentNullException.

是否有必要在模型绑定后以特殊方式标记对象,以便实体框架将进行延迟加载?我怎样才能做到这一点?

显然,我可以使用第二个存储库testChildRepository手动检索子项.但是(a)感觉不对,(b)导致我的存储库设置方式出现问题(每个都使用自己的DBContext – 这是一个我还没有达成协议的问题).

解决方法

为了使您的子集合延迟加载,必须满足两个要求:

>父实体必须附加到EF上下文
>您的父实体必须是延迟加载代理

如果通过上下文从数据库加载父实体(并且导航属性是虚拟的以允许代理创建),则满足这两个要求.

如果您不从数据库加载实体但是手动创建它,则可以使用适当的EF函数实现相同的功能:

var parent = context.TestParents.Create();
parent.TestParentID = 1;
context.TestParents.Attach(parent);

使用Create而不是new在这里很重要,因为它创建了所需的延迟加载代理.然后,您可以访问子集合,ID = 1的父节点的子节点将被懒惰地加载:

var children = parent.TestChildren; // no NullReferenceException

现在,默认的模型绑定器对这些特定的EF函数没有任何线索,并且只是用new实例化父级,也不会将它附加到任何上下文.这两个要求都没有得到满足,延迟加载也无法工作.

您可以使用Create()编写自己的模型绑定器来创建实例,但这可能是最糟糕的解决方案,因为它会使您的视图层非常依赖于EF.

如果在模型绑定之后需要子集合,我会在这种情况下通过显式加载来加载它:

// parent comes as parameter from POST action method
context.TestParents.Attach(parent);
context.Entry(parent).Collection(p => p.TestChildren).Load();

如果您的上下文和EF隐藏在存储库后面,则需要一个新的存储库方法,例如:

void LoadNavigationCollection<TElement>(T entity,Expression<Func<T,ICollection<TElement>>> navigationProperty) 
    where TElement : class
{
    _context.Set<T>().Attach(entity);
    _context.Entry(entity).Collection(navigationProperty).Load();
}

…其中_context是存储库类的成员.

但是,正如Darin所提到的,更好的方法是绑定ViewModels,然后根据需要将它们映射到您的实体.然后,您可以选择使用Create()实例化实体.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读