如何使用Entity Framework Code-First执行CRUD操作?

6

我很难使用EF Code-first更新和删除多对多关系。

我的模型相当简单:

    public class Issue
    {
        [Key]
        public int      IssueId     { get; set; }
        public int      Number      { get; set; }
        public string   Title       { get; set; }
        public DateTime Date        { get; set; }

        public virtual ICollection<Creator> Creators { get; set; }
    }

    public class Creator
    {
        [Key]
        public int      CreatorId   { get; set; }
        public string   FirstName   { get; set; }
        public string   MiddleName  { get; set; }
        public string   LastName    { get; set; }

        public virtual ICollection<Issue> Issues { get; set; }
    }

    public class Icbd : DbContext
    {
        public DbSet<Issue> Issues { get; set; }
        public DbSet<Creator> Creators { get; set; }
    }

我一直不知道如何使用EF上下文来更新多对多关系。在我的插入/更新操作中,我有这段代码:

    [HttpPost]
    public ActionResult EditIssue ( Issue issue, int[] CreatorIds )
    {
        if(CreatorIds == null)
            ModelState.AddModelError("CreatorIds", "Please specify at least one creator");

        if(ModelState.IsValid)
        {
            // insert or update the issue record (no relationship)
            if (issue.IssueId == 0)
                db.Issues.Add(issue);
            else
                db.Entry(issue).State = System.Data.EntityState.Modified;
            db.SaveChanges();

            // delete - delete current relationships
            if (issue.Creators != null)
            {
                issue.Creators = new List<Creator>();
                db.Entry(issue).State = System.Data.EntityState.Modified;
                db.SaveChanges();
            }

            // insert - get creators for many-to-many realtionship
            issue.Creators = db.Creators.Where(x => CreatorIds.Contains(x.CreatorId)).ToList();
            db.Entry(issue).State = System.Data.EntityState.Modified;
            db.SaveChanges();

            return RedirectToAction("Index");
        }

        IssueEditModel issueEdit = new IssueEditModel{
            Creators = db.Creators.ToList(),
            Issue = issue,
        };
        return View(issueEdit);
    }

我可以插入 Issues,也可以插入新的 issue.Creators,但是当我尝试删除当前的issue.Creators以便插入新的时,issue.Creators 总是为 null,因此它永远不会更新为空列表。我不明白这个问题。issue.Creators 中有记录,因为当代码继续插入新的创建者时,我会得到如下错误:

Violation of PRIMARY KEY constraint 'PK__CreatorI__13D353AB03317E3D'. Cannot insert duplicate key in object 'dbo.CreatorIssues'.
The statement has been terminated. 

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK__CreatorI__13D353AB03317E3D'. Cannot insert duplicate key in object 'dbo.CreatorIssues'.
The statement has been terminated.

Source Error: 


Line 59:    issue.Creators = db.Creators.Where(x => CreatorIds.Contains(x.CreatorId)).ToList();
Line 60:    db.Entry(issue).State = System.Data.EntityState.Modified;
Line 61:    db.SaveChanges();
Line 62:
Line 63:    return RedirectToAction("Index");

我该如何使issue.Creators准确显示当前关系,以便我可以删除它们?

更新:工作控制器

    [HttpPost]
    public ActionResult EditIssue ( Issue issue, int[] CreatorIds )
    {
        if(CreatorIds == null)
            ModelState.AddModelError("CreatorIds", "Please specify at least one creator");

        if(ModelState.IsValid)
        {
            // insert or update the issue record (no relationship)
            if (issue.IssueId == 0)
                db.Issues.Add(issue);
            else
                db.Entry(issue).State = System.Data.EntityState.Modified;
            db.SaveChanges();

            // insert and update many to many relationship
            issue = db.Issues.Include("Creators").Where(x => x.IssueId == issue.IssueId).Single();
            issue.Creators = db.Creators.Where(x => CreatorIds.Contains(x.CreatorId)).ToList();
            db.Entry(issue).State = System.Data.EntityState.Modified;
            db.SaveChanges();

            return RedirectToAction("Index");
        }

        IssueEditModel issueEdit = new IssueEditModel{
            Creators = db.Creators.ToList(),
            Issue = issue,
        };
        return View(issueEdit);
    }

嗯...问题 = db.Issues 应该自动包含创建者,而不需要单独调用,我想。在 issue = db.issues..... 行后包括一个 int count = issue.Creators.Count()。 - Adam Tuliper
2个回答

1

模型绑定器不会自动为您加载它们 - 除非您正确设置了绑定,否则来自视图的问题对象不会神奇地包含它们。

没有看到您的视图,无法确定原因,但可以肯定的是,您将不得不加载它们,然后删除它们。您可以加载一个新的问题对象,然后执行TryUpdateModel(issues)将表单值更新到该模型中。然后删除每个issues.Creators(如果这是预期的操作)


 if(ModelState.IsValid)
 {
var issueFromDb = db.Issues.Where(x => criteria here); bool updateSuccessful = TryUpdateModel(issueFromDb); foreach(var creator in issueFromDb.Creators) { //delete creator if thats what you want }

但是,如果您只想让所有创建者返回页面而不加载,请查看绑定到可枚举类型。有很多关于此的帖子,这里是其中之一:ASP.NET MVC3 Model Binding using IEnumerable (Cannot infer type from)

如果关系存在,则创建者应该通过加载问题自动加载。您不需要仅加载创建者。像我在上面的编辑中所做的那样,加载完整模型以确保其正常工作。您的代码“应该”可以正常工作,但有可能需要使用.Include(“Creators”)尝试一下:

var testIssue = from o in db.Issues.Include("Creators") 
where o.IssueId == issue.IssueId 
select o;
foreach(var creator in testIssue.Creators)
{
//check creator
}

这将让您知道“Creators”是否正在正确加载。


当我从数据库中获取issueissue = db.Issues.FirstOrDefault(x => x.IssueId == issue.IssueId))时, issue.Creators 总是空的。这是否是延迟加载的副作用? - quakkels
感谢您提供额外的 var testIssue.... 代码。它指出 testIssue.Creators 不合法--System.Linq.IQueryable<InternetComicsDatabase.Models.Issue>' 没有对 'Creators' 的定义,也没有接受类型为 'System.Linq.IQueryable<InternetComicsDatabase.Models.Issue>' 的第一个参数的 'Creators' 扩展方法。 - quakkels
嗯,好的,尝试在db.Creators上执行一个查询,其中issueid = issue.IssuedId,类似于这样:var creators = from o in db.Creators where o.IssueId=issue.IssueId select o。 - Adam Tuliper
Creators没有IssueId,因为它是多对多的关系。根据我的模型,EF CodeFirst自动创建了桥接表,并且<em>应该</em>能够理解关联。非常令人沮丧。 - quakkels
我之前可能在你的.Include()代码中输入有误。现在它已经可以工作了...我会更新我的问题并展示最终结果,同时我会将你的帖子标记为答案,因为你推荐了.Include() - quakkels

0
您在 issue 类中缺少关键标签(IssueId)。
一旦您找到重复的记录,可能需要手动进入数据库删除这些行。

关键标签没有起作用...我相信Code-First的约定自动知道IssueId是关键字,因为它正确地生成了表。 - quakkels

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接