15

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

你好。我目前正在使用 EF4 CodeFirst 类。在我的测试项目中,我有两个课程,作者和书(作者有书)。我想要做的是我的 Author 类中有一个 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”的属性“书籍”,因为该集合已设置为 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 没有设置为新的,但是我得到了第一个异常。那么如何在 EF 中首先使用代码完成此操作?

也许我应该补充一点,它可能会与我的 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();
    }
}
4

6 回答 6

30

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

更好的方法是修改您的 POCO 类,以便它们在其 get 访问器中实例化集合属性,而不是在构造函数中。这是原始的 Author 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);
    }
}

在上面的代码中,集合属性不再是自动的,而是有一个支持字段。最好让 setter 受保护,以防止任何代码(代理除外)随后修改这些属性。您会注意到不再需要构造函数并已将其删除。

于 2012-03-28T17:37:07.490 回答
7

Dejan.S 提到的关于使用“列表”初始化集合的问题仍然出现在 EF5 RTM 和 ASP.NET 4.5 RTM 中:

列出初始化问题

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

于 2012-08-26T22:40:55.110 回答
6

virtual在所有属性上对我来说都是问题..当我将它扩展到我的项目时,不明白为什么它会产生这样的影响,但事实就是这样..希望这对遇到同样问题的人有所帮助..

于 2010-10-05T21:26:11.887 回答
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集合中 - 无需将书籍添加到集合中并将作者添加到书中,因为实体框架应该处理这一点。

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

HTH,
查尔斯

于 2010-10-04T22:01:47.397 回答
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);
    }
}
于 2013-01-20T17:38:37.340 回答
0

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

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

var ctx = new MyDbContext();
ctx.Configuration.ProxyCreationEnabled = false;
于 2014-08-12T22:34:06.837 回答