Asked by:
Code First EF4 with N-Tier.. I'm really stuck on this one issue

Question
-
Hi. I been learning EF4 the last days. I set my mind on using the POCO T4 until I found out about Code First CTP4 and that was like YES. So I started trying it on a simple asp.net mvc 2 project and it worked like great but then I implemented this to my 3-tier model with services, repositories And DI to find out that It wont work. I'm so new to this that I have no clue what I'm missing or where to start looking. I'll try to explain it as good as I can.
First of all this is the exception I'm getting.
The property 'Books' on type 'Author_4CF5D4EE954712D3502C5DCDDAA549C8E5BF02A0B2133E8826A1AC5A40A15D2A' cannot be set because the collection is already set to an EntityCollection.
Project got Author and Book classes looking like this. They are in my Project.Model class library
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>(); } } public class Book { public virtual int BookId { get; set; } public virtual string Title { get; set; } public virtual Author Author { get; set; } }
in my Project.Data I got the DbContext and repository.
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(); } }
public class AuthorRepository : IAuthorRepository { private EntityContext _context; public AuthorRepository(IUnitOfWork unitOfWork) { if (unitOfWork == null) throw new ArgumentException("unitOfWork"); _context = unitOfWork as EntityContext; } ...................... }
I really hope there is somebody that knows what to do. I'm gone build a Large scale project in about 2 days and really want to work with the Code first approach instead of the POCO T4 generated classes.
Thanks in advance for any tips and answers
Tuesday, October 5, 2010 2:02 AM
All replies
-
Djean, I am not an expert in code first, but I did notice that you shoul make some changes to your model.
Remove virtual on the fields that will actually be in the table in sql server. So AuthorID will be an actual field, so it does not need to be virtual. Same goes for Name. The Books collection is virtual. BookID and Title should not be virtual.
There is no need for the Author constructor to construct a Books collection, the framework will do it for you.
public class Author
{
public int AuthorId { get; set; }
public string Name { get; set; }public virtual ICollection<Book> Books { get; set; }
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public virtual Author Author { get; set; }
}I think the only thing you need in your dbcontext class is this.
public class EntityContext : DbContext, IUnitOfWork
{
public DbSet<Author> Authors { get; set; }
public DbSet<Book> Books { get; set; }
}Now you can do things like this:
EntityContext.Authors.Add(new {name = "Djean"}); EntityContext.Authors.Add(new {name = "Billy"}); EntityContext.Books.Add(new {Title = "My History", Author = EntityContext.Authors.Find(1) }); EntityContext.Books.Add(new {Title = "The Future", Author = EntityContext.Authors.Find(1) }); EntityContext.Books.Add(new {Title = "Great Moments", Author = EntityContext.Authors.Find(2) }); EntityContext.Books.Add(new {Title = "Markets", Author = EntityContext.Authors.Find(2) }); EntityContext.SaveChanges();
I am sure Rowan will chime in here, as he is the local Expert.
We are all stuggling in the fog here in this early beta, so stick with it. I love it.
HTH,
Terrence
Tuesday, October 5, 2010 2:29 AM -
Hi,
If you make all your properties virtual then EF will generate proxy classes at runtime that derives from your POCO classed, these proxies allow EF to find out about changes in real time rather than having to capture the original values of your object and then scan for changes when you save (this is obviously has performance and memory usage benefits but the difference will be negligible unless you have a large number of entities loaded into memory). These are known as 'change tracking proxies', if you make your navigation properties virtual then a proxy is still generated but it is much simpler and just includes some logic to perform lazy loading when you access a navigation property.
Because your original code was generating change tracking proxies, EF was replacing your collection property with a special collection type to help it find out about changes. Because you try and set the collection back to a simple list in the constructor you are getting the exception.
Unless you are seeing performance issues I would follow Terrence's suggestion and just remove 'virtual' from your non-navigation properties.
~Rowan
- Proposed as answer by Rowan MillerModerator Wednesday, October 6, 2010 8:06 PM
Wednesday, October 6, 2010 8:06 PMModerator -
Rowan, this question does not have to do with Djean's problem.
Should I be making all of my properties Virtual? I am a little confused as to when I should, although your explanation above helps.
It sounnds like if I want all of the majic form CF EF, I should be making them all virtual.
Thanks.
Tuesday, October 12, 2010 3:59 PM -
Hi Terrence,
I would suggest only making your navigation properties virtual as it tends to keep things simpler.
If you are bringing a large amount of data into memory and need to improve performance then you could consider making everything virtual.
~Rowan
Wednesday, October 13, 2010 6:25 AMModerator -
Hey Ian, that was helpful. Keep up the good work.
Thanks, TerrenceTuesday, February 22, 2011 5:46 AM