Code First添加到集合中?如何使用Code First和存储库?

时间:2010-10-04 20:37:17

标签: entity-framework asp.net-mvc-2 entity-framework-4 code-first

编辑:这只发生在具有存储库的大型项目中。有没有人使用EF4和CodeFirst方法并使用存储库?请告诉我。

您好。我目前正在使用EF4 CodeFirst Classes。在我的测试项目中,我有两个类,作者和书(作者得到书)。我想要做的是我在我的作者类中有一个AddBook,但这似乎不能像我不能将它添加到集合中一样..这里是我的类和两个不同的例外。

 public class Book
{
    public virtual int BookId { get; set; }
    public virtual string Title { get; set; }
    public virtual Author Author { get; set; }
}

public class Author
{
    public virtual int AuthorId { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<Book> Books { get; set; }

    public Author()
    {
        Books = new Collection<Book>();
    }

    public void AddBook(Book book)
    {
        book.Author = this;
        Books.Add(book);
    }
}

例外:无法设置类型为'Author_4CF5D4EE9​​54712D3502C5DCDDAA549C8E5BF02A0B2133E8826A1AC5A40A15D2A'的属性'Books',因为该集合已设置为EntityCollection。

我将Author类更改为此

public class Author
{
    public virtual int AuthorId { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<Book> Books { get; set; }

    public void AddBook(Book book)
    {
        book.Author = this;
        Books.Add(book);
    }
}
  

异常:未将对象引用设置为   一个对象的实例。

     因为集合无法设置

  已经设置为EntityCollection。

这很自然,因为Collection没有设置为new,但是我得到了第一个异常。那么如何在EF中首先完成代码?

也许我应该补充说我的It可能会与我的DbSet碰撞?

public class EntityContext : DbContext, IUnitOfWork
{
    public DbSet<Author> Authors { get; set; }
    public DbSet<Book> Books { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.IncludeMetadataInDatabase = false;
    }

    public void Save()
    {
        SaveChanges();
    }
}

6 个答案:

答案 0 :(得分:30)

通过阻止实体框架创建更改跟踪代理,从集合属性中删除“virtual”关键字可解决此问题。但是,对于许多人来说,这不是一个解决方案,因为更改跟踪代理可以非常方便,当您忘记在代码中的正确位置检测到更改时,可以帮助防止出现问题。

更好的方法是修改POCO类,以便它们在get访问器中而不是在构造函数中实例化集合属性。这是原始的作者POCO类,经过修改以允许更改跟踪代理创建:

public class Author
{
    public virtual int AuthorId { get; set; }
    public virtual string Name { get; set; }

    private ICollection<Book> _books;

    public virtual ICollection<Book> Books
    {
        get { return _books ?? (_books = new Collection<Book>()); }
        set { _books = value; }
    }

    public void AddBook(Book book)
    {
        book.Author = this;
        Books.Add(book);
    }
}

在上面的代码中,collection属性不再是自动的,而是具有支持字段。如果您保护setter更好,防止任何代码(代理除外)随后修改这些属性。您会注意到构造函数不再是必需的并且已被删除。

答案 1 :(得分:6)

关于所有属性的

virtual对我来说是个问题。当我把它扩展到我的项目时,不要理解为什么它有这样的影响但是它是怎么回事..希望这有助于任何得到同样问题的人......

答案 2 :(得分:6)

Dejan.S上面提到的关于使用'List'来初始化集合的问题仍然发生在EF5 RTM和ASP.NET 4.5 RTM中:

List init issue

但是,如果您从主键中删除虚拟关键字(例如上面的screencap中的 UserId ),它就可以了!所以看起来你的所有属性都可以是虚拟的,除了主键。

答案 3 :(得分:4)

我的代码首先工作,但我的代码唯一(主要)差异是我将它初始化为列表而不是集合...所以我的代码读取如下内容:

public class Author
{
    public virtual int AuthorId { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<Book> Books { get; set; }

    public Author()
    {
        Books = new List<Book>();
    }
}

另外,我只是直接添加到Books集合中 - 没有必要将该书添加到集合中并将作者添加到书中,因为实体框架应该处理它。

如果这对您不起作用。让我知道。

HTHS,
查尔斯

答案 4 :(得分:2)

尝试使用此签名。我希望这有效。

public class Author
{
    public virtual int AuthorId { get; set; }
    public virtual string Name { get; set; }

    private ICollection<Book> _books;

    public virtual ICollection<Book> Books
    {
        get { return _books ?? (_books = new HashSet<Book>()); } // Try HashSet<N>
        set { _books = value; }
    }

    public void AddBook(Book book)
    {
        book.Author = this;
        Books.Add(book);
    }
}

答案 5 :(得分:0)

此实例中的问题是正在生成更改跟踪代理,因为类上的所有属性都标记为虚拟。按照惯例,这会触发更改跟踪代理生成。

因此,您可以从一个或多个属性中删除虚拟关键字,或者只是告诉上下文不要按Working with POCO Entities生成更改跟踪代理。

var ctx = new MyDbContext();
ctx.Configuration.ProxyCreationEnabled = false;