如何将自动生成的ID插入关联表?

时间:2015-11-30 23:39:28

标签: asp.net-mvc-5 ef-code-first entity-framework-6

我正在尝试编写一个可以从各种烹饪网站上删除食谱和配料信息的应用程序。它几乎按照我想要的方式工作,因为我可以成功地将配方插入配方表中,并将配料插入到成分表中,但我无法填充关联表。

我的大多数项目都遵循this tutorial,但它没有演示如何插入关联表。

我只为Recipe创建了一个控制器,其中Create函数接受您想要的配方的URL。我不想直接操作Ingredient表中的数据,所以我没有为它创建一个控制器。

我创建了三个模型类:Recipe,Ingredient和关联表RecipeIngredient。 Recipe和RecipeIngredient之间存在一对多的关系,以及Ingredient和RecipeIngredient之间的一对多关系。

这些是我的模特:

public class Recipe
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string URL { get; set; }
    public DateTime DateTimeCreated { get; set; }

    public virtual ICollection<RecipeIngredient> RecipeIngredients { get; set; }
}

public class Ingredient
{
    public int ID { get; set; }
    public string Name { get; set; }
    public DateTime DateTimeCreated { get; set; }

    public virtual ICollection<RecipeIngredient> RecipeIngredients { get; set; }
}

public class RecipeIngredient
{
    public int ID { get; set; }
    public int RecipeID { get; set; }
    public int IngredientID { get; set; }
    public DateTime DateTimeCreated { get; set; }

    public virtual Recipe Recipe { get; set; }
    public virtual Ingredient Ingredient { get; set; }
}

这是我的DbContext:

public class RecipeListContext : DbContext
{
    public RecipeListContext() : base("RecipeListContext")
    {
    }

    public DbSet<Recipe> Recipes { get; set; }
    public DbSet<RecipeIngredient> RecipeIngredients { get; set; }
    public DbSet<Ingredient> Ingredients { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

这是我控制器的相关代码:

public class RecipesController : Controller
{
    private RecipeListContext db = new RecipeListContext();

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "URL")] Recipe recipe)
    {
        if (ModelState.IsValid)
        {
            Ingredient ingredient = new Ingredient();
            RecipeIngredient recipeIngredient = new RecipeIngredient();

            var htmlWeb = new HtmlWeb();
            var document = htmlWeb.Load(recipe.URL);

            //This is the route to the recipe and ingredients for 
            //allrecipes.com
            var recipeName = document.DocumentNode.SelectNodes("//h1[@class='recipe-summary__h1']");
            var ingredients = document.DocumentNode.SelectNodes("//span[@itemprop='ingredients']");

            //I populate the Name and DateTimeCreated fields for Recipe here
            recipe.Name = recipeName[0].InnerText;
            recipe.DateTimeCreated = DateTime.Now;

            db.Recipes.Add(recipe);
            db.SaveChanges();

            for (var i = 0; i < ingredients.Count; i++)
            {
                ingredient.Name = ingredients[i].InnerText;
                ingredient.DateTimeCreated = DateTime.Now;
                db.Ingredients.Add(ingredient);
                db.SaveChanges();

                //Here I tried to populate the RecipeIngredient object with 
                //the ID from the Ingredient object, but since that ID gets 
                //created in the database upon insertion, ingredient.ID is 
                //just null. I'm not sure how to access this ID in order to 
                //insert it into RecipeIngredient and I'm not even sure if 
                //this is the right (or best) approach to this problem.

                //recipeIngredient.RecipeID = recipe.ID;
                //recipeIngredient.IngredientID = ingredient.ID;
                //db.RecipeIngredients.Add(recipeIngredient);
                //db.SaveChanges();
            }

            return RedirectToAction("Index");
        }

        return View(recipe);
    }

Entity Framework中是否有一些函数允许我使用从插入到两个主表中创建的自动生成的ID来填充关联表?还是有其他解决方案吗?

编辑: 有三个问题阻碍了我的应用程序按预期工作。

  1. 我需要取消注释将ingredient.ID和recipe.ID分配给recipeIngredient外键的代码。

  2. 我需要在for循环中实例化配方和成分对象,以便它们在for循环的每次传递开始时重新初始化。我收到了参照完整性错误而没有重新初始化。

  3. 我需要在recipeIngredients中填充DateTimeCreated字段。我忘了这样做,这导致数据库中的数据类型不匹配错误,因为空值转换为01/01/0001或类似的,超出了DateTime支持的日期范围。

2 个答案:

答案 0 :(得分:1)

我相信EF应该用身份更新您的实体对象:

试试这个

db.Recipes.Add(recipe);
db.SaveChanges();
int newPK = recipe.ID;

答案 1 :(得分:0)

有三个问题阻止我的应用程序按预期工作。

  1. 我需要取消注释将ingredient.ID和recipe.ID分配给recipeIngredient外键的代码。

  2. 我需要在for循环中实例化配方和成分对象,以便它们在for循环的每次传递开始时重新初始化。我收到了参照完整性错误而没有重新初始化。

  3. 在RecipeIngredients中填充了DateTimeCreated字段,因为我之前忘记这样做。

  4. 我的更新和工作代码如下:

        public ActionResult Create([Bind(Include = "URL")] Recipe recipe)
        {
            if (ModelState.IsValid)
            {
                var htmlWeb = new HtmlWeb();
                var document = htmlWeb.Load(recipe.URL);
    
                var recipeName = document.DocumentNode.SelectNodes("//h1[@class='recipe-summary__h1']");
                var ingredients = document.DocumentNode.SelectNodes("//span[@itemprop='ingredients']");
    
                recipe.Name = recipeName[0].InnerText;
                recipe.DateTimeCreated = DateTime.Now;
    
                db.Recipes.Add(recipe);
                db.SaveChanges();
    
                for (var i = 0; i < ingredients.Count; i++)
                {
                    //Moved these next two lines into the for loop
                    Ingredient ingredient = new Ingredient();
                    RecipeIngredient recipeIngredient = new RecipeIngredient();
    
                    ingredient.Name = ingredients[i].InnerText;
                    ingredient.DateTimeCreated = DateTime.Now;
                    db.Ingredients.Add(ingredient);
                    db.SaveChanges();
    
                    //uncommented this code
                    recipeIngredient.RecipeID = recipe.ID;
                    recipeIngredient.IngredientID = ingredient.ID;
                    //populated the DateTimeCreated field as I had forgotten
                    //to do so already
                    recipeIngredient.DateTimeCreated = DateTime.Now;
                    db.RecipeIngredients.Add(recipeIngredient);
                    db.SaveChanges();
                }
    
                return RedirectToAction("Index");
            }
    
            return View(recipe);
        }