4

我只是在学习 MVC 并结合实体框架处理它的无状态性质。我的问题是,有没有更优雅的方式来处理下面的场景?

我有两个 POCO 实体:

public class Contest
{
    public long ID { get; set; }
    ....   .....
    public ICollection<ContestPrize> Prizes { get; set; }
}

public class ContestPrize
{
    public long ID { get; set; }

    public Contest Contest { get; set; }

}

然后我有一个 MVC 视图,它显示了比赛和与之相关的所有奖品。在该视图中,我有一个“创建奖品”链接来创建一个新奖品,它将比赛的 ID 传递给 ContestPrize 控制器,如下所示:

@Html.ActionLink("Create Prize", "Create", "ContestPrizes", new { ContestId = Model.ID }, null)

ContestId 作为提交新奖品的表单中的隐藏字段保留,以便我可以在创建时检索它。奖品控制器上的创建方法如下:

    [HttpPost]
    public ActionResult Create(int ParentID, ContestPrize ContestPrize)
    {


        if (ModelState.IsValid)
        {

            db.ContestPrizes.Add(ContestPrize);
            db.SaveChanges();
            return RedirectToAction("Index", "Contests", ParentID);
        }

        return View(ContestPrize);
    }

这就是我遇到一些我不喜欢的丑陋的地方。以下是问题:

  1. 测试ModelState.IsValid失败。这是因为父级的导航属性在模型上为空。它没有被填充。我可以跳过对模型的验证并直接进入数据库,但是有没有办法在不执行下面的 #2 的情况下填充它?
  2. 此时未填充父属性,但我确实有来自隐藏表单字段的 ParentID。由于一切都是无状态的,因此 db 对象没有父对象(这很好)。因此,我可以执行另一个数据库读取以使用 parentID 获取父对象,然后执行Parent.Prizes.Add(ContestPrize)or ContestPrize.Contest = Parent。但我真的不喜欢这样,因为当我拥有创建子记录所需的所有信息时,我正在做一个额外的数据库访问来读取父级。

那么,Entity Framework 有没有办法只设置父对象的 ID 并保存子对象而无需检索父对象?或者...理想情况下,有没有办法使用 MVC 在创建时使用父对象预填充新模型,因为在进入创建视图之前我确实有父对象?在 ViewData 中传递父对象以使其可用会有多糟糕?

我可以通过重新加载父母来完成这项工作。但如果可能的话,我想避免它。

想法?

更新: 添加ContestID到 ContestPrize POCO,然后[ForeignKey("ContestID")]按照@NSGaga 的建议装饰导航属性完美地工作,并消除了一些混乱的 ParentID 传递问题。新的 POCO 如下所示:

public class Contest
{
    public long ID { get; set; }
    ....   .....
    public ICollection<ContestPrize> Prizes { get; set; }
}

public class ContestPrize
{
    public long ID { get; set; }

    public long ContestID { get; set; }

    [ForeignKey("ContestID")]
    public Contest Contest { get; set; }

}

我可以消除在视图中传递的 ParentID 并将其设置为隐藏字段,因为 MVC 中的数据绑定负责新实体上的 ContestID(我仍然必须将 id 传递到初始视图中,但仅此而已。它是刚从那里绑定)。Controller 上的新 Create 操作如下所示:

[HttpPost]
public ActionResult Create(ContestPrize ContestPrize)
{
    if (ModelState.IsValid)
    {
        db.ContestPrizes.Add(ContestPrize);
        db.SaveChanges();
        return RedirectToAction("Index", "Contests", ContestPrize.ContestID);
    }

    return View(ContestPrize);
}

干净简单,没有乱七八糟的。我喜欢。

4

2 回答 2

3

如果您使用“代码优先”(所有事情都建议),那么您可以执行类似...

public class ContestPrize
{
    public long ID { get; set; }

    public long ContestID { get; set; }
    public Contest Contest { get; set; }
}

这应该创建(按照惯例)并将 ContestID 映射到 FK。在某些复杂的情况下,您可能需要在 fluent 代码中指定该关系,但这通常就足够了。

然后,您可以通过ContestID填写来引用父级 - 然后执行Add

这是粗略的笔触,如果您需要更多信息,请告诉我。

于 2013-04-08T20:37:34.597 回答
1

您可能不应该直接将实体用作视图模型。我建议使用这样的东西:

// Entitiy Type
public class ContestPrize { ... }

// ViewModel Type
public class ContestPrizeViewModel { ... }

// In your controller
[HttpPost]
public ActionResult Create(int ParentID, ContestPrizeViewModel ContestPrize)
{
    if (ModelState.IsValid)
    {
        var entity = db.Create<ContestPrize>();
        entity.ContestID = ParentID;
        entity.Blah = ContestPrize.Blah
        db.ContestPrizes.Add(entity);
        db.SaveChanges();
        return RedirectToAction("Index", "Contests", ParentID);
    }

    return View(ContestPrize);
}

这使您可以轻松地将域逻辑与视图逻辑分开。它可能需要额外的代码,但它是一种更强大的做事方式。

于 2013-04-08T20:38:35.943 回答