I am using Entity framework 4.3 code first to persist a domain model. The domain model consists of abstract and concrete classes. I see a ArgumentNullException thrown when a concrete class returned by the entity framework first tries to access a collection of objects in the base class.
For example a domain model consists of the abstract classes
public abstract class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Attibute> attributes { get; set; }
public string DoSomething()
{
return "I'm all fooey";
}
}
public abstract class Attibute
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Foo> Foos { get; set; }
}
I then have derived classes like
public class BadFoo : Foo
{
public List<BadAttribute> BadAttribs()
{
return base.attributes.OfType<BadAttribute>().ToList<BadAttribute>();
}
public void BeBad()
{
foreach (var a in BadAttribs())
Console.WriteLine(Name + ": Being bad => " + a.DoingBad());
}
}
public class GoodFoo : Foo
{
public List<GoodAttribute> GoodAttribs()
{
return base.attributes.OfType<GoodAttribute>().ToList<GoodAttribute>();
}
public void BeGood()
{
foreach (var a in GoodAttribs())
Console.WriteLine(Name + ": Being good => " + a.DoingGood());
}
}
public class BadAttribute : Attibute
{
public string DoingBad()
{
return Name + " : Start being bad";
}
}
public class GoodAttribute : Attibute
{
public string DoingGood()
{
return Name + " : Start being Good";
}
}
public class AppearenceAttribute : Attibute
{
public string Doing()
{
return Name + " : defining ones appearance";
}
}
I then set up my db context as follows:
public class EFDbContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public DbSet<Attibute> Attributes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class EFDbContextInitializer : DropCreateDatabaseAlways<EFDbContext>
{
protected override void Seed(EFDbContext context)
{
List<Attibute> attribs = new List<Attibute>
{
new BadAttribute { Name = "Gluttony" },
new BadAttribute { Name = "Greed" },
new GoodAttribute { Name = "Honesty" },
new GoodAttribute { Name = "Humility" },
new AppearenceAttribute { Name = "Colour" }
};
attribs.ForEach(a => context.Attributes.Add(a));
context.SaveChanges();
BadFoo badOne = new BadFoo { Name = "badOne", attributes = new List<Attibute>)};
context.Attributes.OfType<BadAttribute>().ToList().ForEach(a => badOne.attributes.Add(a));
context.Attributes.OfType<AppearenceAttribute>().ToList().ForEach(a => badOne.attributes.Add(a));
context.Foos.Add(badOne);
context.SaveChanges();
GoodFoo goodOne = new GoodFoo { Name = "GoodOne", attributes = new List<Attibute>() };
context.Attributes.OfType<GoodAttribute>().ToList().ForEach(a => goodOne.attributes.Add(a));
context.Attributes.OfType<AppearenceAttribute>().ToList().ForEach(a => goodOne.attributes.Add(a));
context.Foos.Add(goodOne);
context.SaveChanges();
base.Seed(context);
}
}
I then use my model as follows:
Database.SetInitializer<EFDbContext>(new EFDbContextInitializer());
EFDbContext context = new EFDbContext();
var foos = context.Foos.OfType<BadFoo>().ToList();
foreach (var f in foos)
f.BeBad();
var foos2 = context.Foos.OfType<GoodFoo>().ToList();
foreach (var f in foos2)
f.BeGood();
Console.ReadKey();
The exception is thrown when a concrete class derived from Foo first tries to use the collection in Foo base class. In this case the first time an instance of BadFoo calls the BeBad() method.
I am not sure if it's because EF lazy loading the collection, the way C# deals with abstract/concrete classes or the domain model just sucks anyone care to explain/enlighten me as to the problem(s)?
As a side note if I replace the way the base attributes are accessed in the derived attribute classes BadFoo and GoodFoo to be properties which return a type filtered list of the base class collection property like :
public class GoodFoo : Foo
{
public List<GoodAttribute> GoodAttribs()
{
return base.attributes.OfType<GoodAttribute>().ToList<GoodAttribute>();
}
}
EF adds an _Id column to the table why is that?
base.attributeswiththis.attributesfix anything? - bricelam