创建与现有父项关系的新子项在dbContext.save()中创建新父项

时间:2015-03-15 16:15:23

标签: c# entity-framework model-view-controller one-to-many dbcontext

我有一对多,使用代码第一实体框架模型创建的父子关系。

以下代码创建一个具有对现有父级的引用的新子代的结果是我最终得到一个新的父条目。

我已经看到了一个建议的解决方案,即将父级附加到上下文或更改它的EntityState。然而,我不认为这是有效的,因为这两种模型存在于不同的背景下。

对于我来说,不同的背景对我来说似乎总是很奇怪,但我在某个地方读到这样做是为了让每个模型保持模块化以保持这种状态。 (即使他们使用相同的连接字符串等)

所以问题是: 如何确保Child与现有父级相关联,而不是与新创建的父级相关联?

即使不同的dbContexts没有导致问题,这是明智的吗?

我的控制器代码:

[HttpPost]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult Create([Bind(Include = "Information_Id,Information_Title,Information_LinkText,Information_URLBody")] Information information, int InformationContainer_Id)
        {
            InformationContainerDBContext dbContainer = new InformationContainerDBContext();

            //information.Information_Container = (from p in dbContainer.InformationContainers where p.InformationContainer_Id == InformationContainer_Id select p).ToList()[0];
            information.Information_Container = dbContainer.InformationContainers.Single(o => o.InformationContainer_Id == InformationContainer_Id);


            if (ModelState.IsValid)

            {
                dbContainer.Entry(information.Information_Container).State = EntityState.Unchanged;

                db.Informations.Add(information);
                db.SaveChanges();


                return RedirectToAction("Index");
            }

            return View(information);
        }

我的模特:

父:

public class InformationContainer
    {
        [Key]
        public int InformationContainer_Id { get; set; }

        [Required]
        public string InformationContainer_Title { get; set; }

       [InverseProperty("Information_Container")]
        public List<Information> Informations{ get; set; }

    }

    public class InformationContainerDBContext : DbContext
    {
        public InformationContainerDBContext()
            : base(ConfigurationManager.ConnectionStrings["DataDBString"].ConnectionString)
        {
        }
        public DbSet<InformationContainer> InformationContainers { get; set; }
    }

子:

public class Information
    {
    [Key]
    public int Information_Id { get; set; }

    [Required]
    public string Information_Title { get; set; }

    [Required]
    public string Information_LinkText { get; set; }

    [Required]
    [AllowHtml]
    public string Information_URLBody { get; set; }

    public InformationContainer Information_Container { get; set; }
}

public class InformationDBContext : DbContext
{
    public InformationDBContext()
        : base(ConfigurationManager.ConnectionStrings["DataDBString"].ConnectionString)
    {
    }
    public DbSet<Information> Informations { get; set; }
}

我的观点:

@model eCommSite.Areas.Admin.Models.Information

@{
    ViewBag.Title = "Information Pages - Create";
}

<script type="text/javascript" src="~/Scripts/tinymce/tinymce.min.js"></script>
<script>tinymce.init({ selector: 'textarea' });</script>


<h2>Information Pages</h2>

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>Create</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Information_Title, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Information_Title, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Information_Title, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Information_LinkText, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Information_LinkText, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Information_LinkText, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Information_Container, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                <select name="InformationContainer_Id">
                    <option value="1">Help</option>
                    <option value="2">Company</option>
                    <option value="3">Information For Parents</option>

                </select>
              </div>
        </div>


        <div class="form-group">
            @Html.LabelFor(model => model.Information_URLBody, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10" style=" height:500px!important; width: 80%;">
                <textarea id=" wysiwyg"
                          style=" height:350px; width: 500px;"
                          name="Information_URLBody"></textarea>

            </div>

        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

3 个答案:

答案 0 :(得分:1)

摆脱额外的DbContext,只有一个。将此属性添加到Information类,使其成为外键:

public int InformationContainer_Id { get; set; }

您的上下文应如下所示:

public class InformationDBContext : DbContext
{
    public InformationDBContext()
        : base(ConfigurationManager.ConnectionStrings["DataDBString"].ConnectionString)
    {
    }
    public DbSet<Information> Informations { get; set; }
    public DbSet<InformationContainer> InformationContainers { get; set; }
}

由于InformationContainer_Id是一个外键,你的控制器代码应该是这样的,它应该可以工作。

[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create([Bind(Include = "Information_Id,Information_Title,Information_LinkText,Information_URLBody")] Information information, int InformationContainer_Id)
{
    if (ModelState.IsValid)
    {
        information.InformationContainer_Id = InformationContainer_Id;
        db.Informations.Add(information);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(information);
}

答案 1 :(得分:0)

您应该将代码更改为:

        [HttpPost]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult Create([Bind(Include = "Information_Id,Information_Title,Information_LinkText,Information_URLBody")] Information information, int InformationContainer_Id)
        {
                if (ModelState.IsValid)        
                {
                    InformationContainerDBContext dbContainer = new InformationContainerDBContext();
                    var infContainer = dbContainer.InformationContainers.Single(o => o.InformationContainer_Id == InformationContainer_Id);

                    infContainer.Informations.Add(information);
                    dbContainer.SaveChanges();


                    return RedirectToAction("Index");
                }
                return View(information);
        }

如果您将导航属性添加到已存在的属性的集合中,它也将被添加到数据库中。

答案 2 :(得分:0)

这里发生了一些事情:

首先,有多个DbContext很疯狂,并且通过一些工作我将它们全部合并。

(如果你正在阅读这篇文章......只有一个DbContext要维护是巨大的,无论你对可能的工作有多么可怕,我强烈建议合并,特别是如果你喜欢因经验不足而只有多重背景的我。)

其次,我正在添加对来自另一个上下文的实体的引用,并且还引用实体而不是它们的id,这会在调用保存更改时创建新实体