ドメイン モデルを永続化するために、最初にエンティティ フレームワーク 4.3 コードを使用しています。ドメイン モデルは、抽象クラスと具象クラスで構成されます。エンティティ フレームワークによって返された具象クラスが最初に基本クラスのオブジェクトのコレクションにアクセスしようとすると、ArgumentNullException がスローされます。
たとえば、ドメイン モデルは抽象クラスで構成されます。
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; }
}
次に、次のような派生クラスがあります
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";
}
}
次に、db コンテキストを次のように設定します。
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);
}
}
次に、次のようにモデルを使用します。
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();
Foo から派生した具象クラスが最初に Foo 基本クラスでコレクションを使用しようとすると、例外がスローされます。この場合、BadFoo のインスタンスが初めて BeBad() メソッドを呼び出します。
EFがコレクションを怠惰にロードしているためか、C#が抽象/具象クラスを処理する方法、またはドメインモデルが問題について説明/啓発するのを気にかけているためかどうかはわかりませんか?
補足として、派生属性クラス BadFoo および GoodFoo で基本属性にアクセスする方法を、次のような基本クラス コレクション プロパティの型フィルターされたリストを返すプロパティに置き換えると、次のようになります。
public class GoodFoo : Foo
{
public List<GoodAttribute> GoodAttribs()
{
return base.attributes.OfType<GoodAttribute>().ToList<GoodAttribute>();
}
}
EF がテーブルに _Id 列を追加するのはなぜですか?