使用MVC 4和实体框架更新相关数据?

时间:2014-01-27 12:46:44

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

所以,我在保存包含相关实体的数据时遇到问题,当我保存它时会创建一个新的关系空白。

例:

实体:

public class Project
{
    public int Id { get; set; }
    public int Code{ get; set; }
    public string Description{ get; set; }

    public virtual Client Client { get; set; }
}


public class Client
{
    public int Id { get; set; }
    public int Code { get; set; }
    public string Name { get; set; }
}

控制器GET:

public ActionResult Create()
{
    PopulateDropDownClienteList(String.Empty); //Returns to ViewBag to create a combobox .in view
    return View();
}

观点:

 @Html.DropDownListFor(m => m.Client.Id, new SelectList(ViewBag.Client_Id, "Id", "Name"), new { Name = "Client.Id" });

Controller POST:

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(string command, Project project)
    {
        try
        {
            if (ModelState.IsValid)
            {
                projectRepository = new ProjeRepository();
                Project pro = projectRepository.ReturnByCode(project.Code);

                if (pro == null)
                    projectRepository.Save(project);
                else
                    projectRepository.Update(project);

                PopulateDropDownClienteList(String.Empty);

                Return View();

            }
            else
            {
                return View(project);
            }
        }
        catch (Exception ex)
        {
            return View();
        }
    }

因此,当我保存数据时,客户端与项目无关。只需创建一个新的空白客户端。

2 个答案:

答案 0 :(得分:1)

你在这里遇到了很多问题,所以让我分解一下。

首先,永远不会抓住Exception (至少不再抛弃它)。关于使用try...catch块有两个非常重要的事情:你应该只将代码包装在你预期异常的地方(不像你在这里完成的几乎整个方法),你应该只捕获特定的异常你期待(不是基本类型Exception)。当您捕获Exception时,将捕获可能从您的代码生成的任何异常,并且在这种情况下,简单地丢弃,这意味着您根本不会知道此代码是否正常工作。

其次,您有一个很好的方法可以生成一个选项下拉列表,但绝不会将用户的选择存储在任何有意义的位置。要了解原因,您需要停下来思考这里发生的事情。 HTML select元素具有字符串值和字符串文本或标签组件。它不支持来回传递完整的对象。我无法看到您的PopulateDropDownClienteList方法的功能,但 应该做的是创建IEnumerable<SelectListItem>,其中每个项目的Text属性设置为任何你想要显示的内容及其Value属性到Client的PK。但是,一旦你有了这个,你需要Project上的一些属性来回发。您的虚拟Client将无法正常工作,因为您需要一个完整的Client实例,您的表单将永远不会有这个实例。所以,你有两个选择:

  1. 实施视图模型以提供给视图(并在帖子中接受)。在该视图模型中,除了所有其他可编辑字段外,您还会添加ClientId类似int类型的内容,并将其绑定到下拉列表中。进入post方法后,将所有发布的值映射到项目实例,然后使用ClientId从数据库中查找客户端。然后,将生成的客户端设置为Client属性的值,并照常保存。

  2. 您稍微更改了数据库。当您只指定虚拟时,实体框架会巧妙地创建一个外键和一个列,以便在幕后为您保存该关系。这很好,但在这样的情况下,你实际上需要访问那个外键列,你就搞砸了。这样就可以明确定义一个属性来保存模型上的关系,并告诉实体框架使用它而不是创建它自己的。

    [ForeignKey("Client")]
    public int ClientId { get; set; }
    public virtual Client Client { get; set; }
    

    有了这个,您现在可以直接使用ClientId,而无需担心填写Client。您再次将下拉列表绑定到ClientId,但现在,您不需要从数据库中显式查找客户端。实体框架只会将ClientId保存到数据库中,然后在将来再次查找项目时根据该值恢复Client

答案 1 :(得分:1)

您的项目保存代码未更新实体,它始终添加一个新实体。

您应该具有类似于以下理由的更新逻辑 -

添加新的FK条目并将其与父记录关联 -

var entity = entities.Students.Where(p => p.Id == "2").First();
entity.StudentContact = new StudentContact() { Contact = "xyz", Id = "2" };

entities.Students.Attach(entity);
var entry = entities.Entry(entity);
// other changed properties
entities.SaveChanges();

使用新的详细信息更新FK记录 -

var entity = entities.Students.FirstOrDefault();
entity.StudentContact.Contact = "ABC";

entities.Students.Attach(entity);
var entry = entities.Entry(entity);
entry.Property(e => e.StudentContact.Contact).IsModified = true;
// other changed properties
entities.SaveChanges();

上面的代码,我有一个与StudentContacts有FK关系的学生记录。我更新了学生的联系信息,然后使用ATTACH将其更新到数据库。