2

为了说明我的问题,假设我有一个数据模型,其中有一组书籍,每本书都有一个或多个草稿,还有一个“当前”草稿。我希望我的模型看起来像这样:

class Book
{
    public int Id { get; set; }

    public string Title { get; set; }

    public virtual ICollection<Draft> Drafts { get; set; }

    public virtual Draft CurrentDraft { get; set; }
}

class Draft
{
    public int Id { get; set; }

    public virtual int BookId { get; set; }
    public virtual Book Book { get; set; }

    public string Description { get; set; }
}

我有一个看起来像这样的 DbContext:

class TestDbContext : DbContext
{
    public DbSet<Book> Books { get; set; }
    public DbSet<Draft> Drafts { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

        base.OnModelCreating(modelBuilder);
    }
}

...还有一个像这样的简单程序:

static void Main(string[] args)
{
    Database.SetInitializer<TestDbContext>(new DropCreateDatabaseAlways<TestDbContext>());

    using (var db = new TestDbContext())
    {
        var book = new Book() { Title = "War and Peace", Drafts = new List<Draft>() };


        var draft1 = new Draft() { Book = book, Description = "First Draft" };

        book.Drafts.Add(draft1);


        var draft2 = new Draft() { Book = book, Description = "Second Draft" };

        book.Drafts.Add(draft2);


        book.CurrentDraft = draft2;


        db.Books.Add(book);

        db.SaveChanges();


        foreach (var b in db.Books)
        {
            Console.WriteLine("Book {0}: {1}", b.Id, b.Title);

            foreach (var d in book.Drafts)
                Console.WriteLine("\tDraft ID {0}: {1} from Book ID {2}", d.Id, d.Description, d.BookId);

            Console.WriteLine("Current draft has ID {0}", b.CurrentDraft.Id);
        }

        Console.ReadKey();
    }
}

当数据库由 EF 创建时,它看起来像这样:

数据库模式

[我对额外的 Book_Id 列并不感兴趣,但如果它有效,我可以接受它。]

可悲的是,当我运行测试程序时,它在调用时失败了SaveChanges,但有一个例外:

保存不为其关系公开外键属性的实体时发生错误。EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅 InnerException。

我尝试在 OnModelCreating 中使用流利的 API,但是当我尝试以这种方式配置它时,它会因为 Books 和 Drafts 之间有两个 FK 关系而感到困惑。我对流利的东西(或一般的EF)不够熟悉,不知道如何正确地做到这一点。

这甚至可能首先在 EF 代码中实现吗?

4

2 回答 2

1

如果我是你,我会在 Draft 中再添加一个属性bool IsCurrent{get;set;}。和

public Draft CurrentDraft { get{return Drafts.SingleOrDefault(c=>c.IsCurrent)} }

在这种情况下,书籍和草稿之间将有一个外键。

于 2012-10-09T17:59:44.357 回答
1

您的内部例外是 EF 无法确定将内容保存到数据库以正确创建内容的顺序。如果您在多个地方调用 save changes 来强制您的测试应用程序工作的顺序:

            var book = new Book() { Title = "War and Peace", Drafts = new List<Draft>() };
            db.Books.Add(book);
            db.SaveChanges();
            var draft1 = new Draft() { Book = book, Description = "First Draft" };
            book.Drafts.Add(draft1);
            var draft2 = new Draft() { Book = book, Description = "Second Draft" };
            book.Drafts.Add(draft2);
            book.CurrentDraft = draft2;
            db.SaveChanges();

关于第二个 book_id - 你可以使用这个流利的:

        modelBuilder.Entity<Book>()
            .HasMany(b => b.Drafts)
            .WithRequired(d => d.Book)
            .HasForeignKey(d => d.BookId);

        modelBuilder.Entity<Book>()
            .HasOptional(b => b.CurrentDraft)
            .WithOptionalDependent()
            .Map(m => m.MapKey("CurrentDraftId"));

产生这个数据库:

在此处输入图像描述

于 2012-10-09T18:15:17.730 回答