Reputation: 1835
I’m using entity framework 5. I’m trying to lazy load an entity collection, I stripped the model to the bare bones to have a simple runable sample.
This is my model:
public class A
{
public int Id { get; set; }
public EntityCollection<B> Bs
{
get { return bs; }
set { bs = value; }
}
private EntityCollection<B> bs;
public A()
{
bs = new EntityCollection<B>();
}
}
public class B
{
public int Id { get; set; }
public A A { get; set; }
}
public class DbModel : DbContext
{
public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<A>()
.HasKey( t => t.Id )
.HasMany(a => a.Bs)
.WithRequired(b => b.A);
modelBuilder.Entity<A>()
.Property(t => t.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<B>()
.HasKey(b => b.Id)
.HasRequired(b => b.A);
modelBuilder.Entity<B>()
.Property( t => t.Id )
.HasDatabaseGeneratedOption( DatabaseGeneratedOption.Identity );
}
This is the test demonstrating my problem:
[TestInitialize]
public void Initialize()
{
model = new DbModel();
model.Configuration.ProxyCreationEnabled = false;
if (model.Database.Exists()) model.Database.Delete();
model.Database.Create();
A a = model.As.Create();
model.As.Add(a);
B b = model.Bs.Create();
a.Bs.Add(b);
model.ChangeTracker.DetectChanges();
model.SaveChanges();
}
[TestMethod]
public void TestMethod1()
{
// arrange
DbModel tModel = new DbModel();
A a = tModel.As.First();
// act
a.Bs.Load();
}
This is the result:
Test method TestProject1.UnitTest1.TestMethod1 threw exception:
System.InvalidOperationException: Requested operation is not allowed when the owner of this RelatedEnd is null. RelatedEnd objects that were created with the default constructor should only be used as a container during serialization.
Upvotes: 1
Views: 653
Reputation: 1835
As it turns out I was using classes from different paradigms of the ADO family. As pointed out by Gert Arnold EF doesn’t work with EntityCollection it uses a plain list for association collections.
public class A
{
public int Id { get; set; }
public List<B> Bs
{
get { return bs; }
set { bs = value; }
}
private List<B> bs = new List<B>();
}
As I did not want the proxies, the objects have no “knowledge” of the model. So the model itself has to be used to load associations for an entity. For this sample I added the following method to the model:
public class DbModel : DbContext
{
public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }
//…
public A LoadAWithAssociations(A targetA)
{
return As.Include("Bs").First(a => a.Id.Equals(targetA.Id));
}
}
I adapted the test to use the new method
[TestMethod]
public void TestMethod1()
{
// arrange
DbModel tModel = new DbModel();
A a = tModel.As.First();
// act
A result = tModel.LoadAWithAssociations(a);
// assert
Assert.IsNotNull(result);
Assert.IsTrue(result.Bs.Count > 0);
}
It gives a green now
Upvotes: 0
Reputation: 109252
EntitySet
is part of the linq-to-sql API. That does not explain this particular exception, but a less than smooth cooperation with DbContext is no surprise. When you use DbContext API it is common to define a 1:n navigation property as an interface like ICollection
:
public virtual ICollection<B> Bs { get; set; }
(virtual
to enable proxy creation for lazy loading)
As an aside: you can use model.Database.Delete()
without checking whether the database exists. And DetectChanges()
right before SaveChanges()
is redundant.
Upvotes: 1
Reputation: 5761
A bit convoluted, this code. Do you really want a class (B) that contains a reference to class A that in turn has a collection of B? Maybe it's just the obfuscated names, but I can't imagine a scenario for an object model like this.
In any case, my instinct would be to look for a circular reference or another condition that is preventing an object property to be instantiated on one of your classes.
Also, you are using Eager loading in the test method using a.Bs.Load(). Lazy Loading is disabled when you set ProxyCreationEnabled = false.
Upvotes: 0