编辑:这只发生在具有存储库的大型项目中。有没有人使用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_4CF5D4EE954712D3502C5DCDDAA549C8E5BF02A0B2133E8826A1AC5A40A15D2A'的属性'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();
}
}
答案 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中:
但是,如果您从主键中删除虚拟关键字(例如上面的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;