Entity framework EF6代码先继承后引用
我首先使用EntityFramework6代码编写DAL。我有一个表每类型继承结构 我有四门课:AbstractMaster、ConcreteMaster、AbstractDetail和ConcreteDetail。直观地说,具体类继承自抽象类,抽象类和具体类的主类和细节类之间存在一对多的关系。每个类型模式的表格是一项要求 如果我向ConcreteMaster实体添加ConcreteDetail并保存更改(DbContext),我将收到一个外键错误。原因是已设置从ConcreteDetail到ConcreteMaster的反向引用,但尚未设置从AbstractDetail到AbstractMaster的反向引用 如果在“抽象”级别删除一对多关系,则测试通过。虽然数据完整性仍然在“具体”级别强制执行,但数据库仍然缺少合法的外键。似乎是一个有效的用例 有什么建议吗 谢谢,Entity framework EF6代码先继承后引用,entity-framework,inheritance,ef-code-first,table-per-type,Entity Framework,Inheritance,Ef Code First,Table Per Type,我首先使用EntityFramework6代码编写DAL。我有一个表每类型继承结构 我有四门课:AbstractMaster、ConcreteMaster、AbstractDetail和ConcreteDetail。直观地说,具体类继承自抽象类,抽象类和具体类的主类和细节类之间存在一对多的关系。每个类型模式的表格是一项要求 如果我向ConcreteMaster实体添加ConcreteDetail并保存更改(DbContext),我将收到一个外键错误。原因是已设置从ConcreteDetail到C
John对主类的集合导航属性使用
ObservableCollection
。为ConcreteMaster
类中每个集合的CollectionChanged
事件实现处理程序方法。这里的想法是,当一个项目添加或删除到一个集合中时,您将从另一个集合中添加/删除相同的项目:
AbstractMaster类:
[Table( "AbstractMaster" )]
public abstract class AbstractMaster
{
public int Id { get; set; }
public virtual ObservableCollection<AbstractDetail> AbstractDetails { get; private set; }
public AbstractMaster()
{
AbstractDetails = new ObservableCollection<AbstractDetail>();
}
}
[Table( "ConcreteMaster" )]
public class ConcreteMaster : AbstractMaster
{
public virtual ObservableCollection<ConcreteDetail> ConcreteDetails { get; private set; }
public ConcreteMaster()
{
ConcreteDetails = new ObservableCollection<ConcreteDetail>();
ConcreteDetails.CollectionChanged += ConcreteDetails_CollectionChanged;
base.AbstractDetails.CollectionChanged += AbstractDetails_CollectionChanged;
}
void AbstractDetails_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
{
var newDetails = new List<ConcreteDetail>();
var oldDetails = new List<ConcreteDetail>();
bool nonConcreteDetailAdded = false;
switch( e.Action )
{
case NotifyCollectionChangedAction.Reset:
var newCollection = sender as ReadOnlyObservableCollection<AbstractDetail>;
nonConcreteDetailAdded = !newCollection.All( ad => ad is ConcreteDetail );
if( !nonConcreteDetailAdded )
{
newDetails.AddRange( e.NewItems.Cast<ConcreteDetail>() );
}
break;
default:
if( null != e.OldItems )
{
oldDetails.AddRange( e.OldItems.Cast<ConcreteDetail>() );
}
if( null != e.NewItems )
{
nonConcreteDetailAdded = !e.NewItems.Cast<AbstractDetail>().All( ad => ad is ConcreteDetail );
if( !nonConcreteDetailAdded )
{
newDetails.AddRange( e.NewItems.Cast<ConcreteDetail>() );
}
}
break;
}
if( nonConcreteDetailAdded )
{
throw new InvalidOperationException( "An object of a type not derived from ConcreteDetail was added to the AbstractDetails property of a ConcreteMaster object's base class" );
}
foreach( var removed in oldDetails )
{
if( ConcreteDetails.Contains( removed ) )
{
ConcreteDetails.Remove( removed );
}
}
foreach( var added in newDetails )
{
if( !ConcreteDetails.Contains( added ) )
{
ConcreteDetails.Add( added );
}
}
}
void ConcreteDetails_CollectionChanged( object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e )
{
var newDetails = new List<AbstractDetail>();
var oldDetails = new List<AbstractDetail>();
switch( e.Action )
{
case NotifyCollectionChangedAction.Reset:
var newCollection = sender as ReadOnlyObservableCollection<AbstractDetail>;
base.AbstractDetails.Clear();
newDetails.AddRange( newCollection );
break;
default:
if( null != e.OldItems )
{
oldDetails.AddRange( e.OldItems.Cast<AbstractDetail>() );
}
if( null != e.NewItems )
{
newDetails.AddRange( e.NewItems.Cast<AbstractDetail>() );
}
break;
}
foreach( var removed in oldDetails )
{
if( base.AbstractDetails.Contains( removed ) )
{
base.AbstractDetails.Remove( removed );
}
}
foreach( var added in newDetails )
{
if( !base.AbstractDetails.Contains( added ) )
{
base.AbstractDetails.Add( added );
}
}
}
}
最后,在ConcreteDetail
类中,在ConcreteMaster
属性的setter中设置base.AbstractMaster
属性,并向base.AbstractMaster
添加事件处理程序,该事件处理程序将在base.AbstractMaster
更改时更新this.ConcreteMaster
:
[Table( "ConcreteDetail" )]
public class ConcreteDetail : AbstractDetail
{
private ConcreteMaster _concreteMaster = null;
public ConcreteMaster ConcreteMaster
{
get
{
return _concreteMaster;
}
set
{
if( value != _concreteMaster )
{
_concreteMaster = value;
base.AbstractMaster = _concreteMaster;
}
}
}
public ConcreteDetail()
{
base.PropertyChanged += ConcreteDetail_PropertyChanged;
}
void ConcreteDetail_PropertyChanged( object sender, PropertyChangedEventArgs e )
{
if( e.PropertyName == "AbstractMaster" )
{
var master = base.AbstractMaster;
if( null == master )
{
_concreteMaster = null;
}
else if( master is ConcreteMaster )
{
_concreteMaster = master as ConcreteMaster;
}
else
{
throw new InvalidOperationException( "AbstractMaster property of a ConcreteDetail object's base class was set to an instance of a class that does not derive from ConcreteDetail" );
}
}
}
}
我已使用以下代码对此进行了测试:
class Program
{
static void Main(string[] args)
{
using( var db = new TestEntities() )
{
var master = new ConcreteMaster();
var details = new[]{
new ConcreteDetail() { Id = 1 },
new ConcreteDetail() { Id = 2 },
new ConcreteDetail() { Id = 3 },
new ConcreteDetail() { Id = 4 }
};
master.AbstractDetails.Add( details[ 0 ] );
master.ConcreteDetails.Add( details[ 1 ] );
details[ 2 ].AbstractMaster = master;
details[ 3 ].ConcreteMaster = master;
db.ConcreteMasters.Add( master );
db.AbstractDetails.Add( details[ 2 ] );
db.ConcreteDetails.Add( details[ 3 ] );
db.SaveChanges();
}
using( var db = new TestEntities() )
{
var concreteMaster = db.ConcreteMasters.Single();
var abstractMaster = db.AbstractMasters.Single();
Action<string, IEnumerable<AbstractDetail>> outputDelegate = ( string header, IEnumerable<AbstractDetail> details ) =>
{
if( details.Count() > 0 )
{
Console.WriteLine( "{0}: {1}", header, string.Join( ", ", details.Select( ad => ad.Id.ToString() ) ) );
}
else
{
Console.WriteLine( "{0}: <empty>", header );
}
};
// 1, 2, 3, 4
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
// remove Id == 4 by way of removing from abstract collection
abstractMaster.AbstractDetails.Remove( abstractMaster.AbstractDetails.Single( ad => ad.Id == 4 ) );
db.SaveChanges();
// 1, 2, 3
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
// remove Id == 3 by way of removing from concrete collection
concreteMaster.ConcreteDetails.Remove( concreteMaster.ConcreteDetails.Single( cd => cd.Id == 3 ) );
db.SaveChanges();
// 1, 2
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
// remove Id == 2 by way of removing AbstractDetail from DbSet<AbstractDetail>
db.AbstractDetails.Remove( abstractMaster.AbstractDetails.Single( ad => ad.Id == 2 ) );
db.SaveChanges();
// 1
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
// remove Id == 1 by wa of removing ConcreteDetail from DbSet<ConcreteDetail>
db.ConcreteDetails.Remove( concreteMaster.ConcreteDetails.Single( cd => cd.Id == 1 ) );
db.SaveChanges();
// <empty>
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
}
var input = Console.ReadLine();
}
}
类程序
{
静态void Main(字符串[]参数)
{
使用(var db=newtestentities())
{
var master=新的ConcreteMaster();
var详细信息=新[]{
新建ConcreteDetail(){Id=1},
新建ConcreteDetail(){Id=2},
新建ConcreteDetail(){Id=3},
新建ConcreteDetail(){Id=4}
};
master.AbstractDetails.Add(details[0]);
master.ConcreteDetails.Add(详情[1]);
详细信息[2]。AbstractMaster=master;
详细信息[3]。混凝土主节点=主节点;
db.ConcreteMasters.Add(master);
db.AbstractDetails.Add(详情[2]);
db.ConcreteDetails.Add(详情[3]);
db.SaveChanges();
}
使用(var db=newtestentities())
{
var concreteMaster=db.ConcreteMasters.Single();
var abstractMaster=db.AbstractMasters.Single();
Action outputDelegate=(字符串头,IEnumerable详细信息)=>
{
如果(details.Count()>0)
{
Console.WriteLine(“{0}:{1}”,标题,string.Join(“,”,details.Select(ad=>ad.Id.ToString());
}
其他的
{
WriteLine(“{0}:”,标头);
}
};
// 1, 2, 3, 4
outputDelegate(“AbstractMaster.AbstractDetails”,AbstractMaster.AbstractDetails);
outputDelegate(“ConcreteMaster.ConcreteDetails”,ConcreteMaster.ConcreteDetails);
//通过从抽象集合中移除,移除Id==4
abstractMaster.AbstractDetails.Remove(abstractMaster.AbstractDetails.Single(ad=>ad.Id==4));
db.SaveChanges();
// 1, 2, 3
outputDelegate(“AbstractMaster.AbstractDetails”,AbstractMaster.AbstractDetails);
outputDelegate(“ConcreteMaster.ConcreteDetails”,ConcreteMaster.ConcreteDetails);
//通过从混凝土集合中移除的方式移除Id==3
concreteMaster.ConcreteDetails.Remove(concreteMaster.ConcreteDetails.Single(cd=>cd.Id==3));
db.SaveChanges();
// 1, 2
outputDelegate(“AbstractMaster.AbstractDetails”,AbstractMaster.AbstractDetails);
outputDelegate(“ConcreteMaster.ConcreteDetails”,ConcreteMaster.ConcreteDetails);
//通过从DbSet中删除AbstractDetail,删除Id==2
Remove(abstractMaster.AbstractDetails.Single(ad=>ad.Id==2));
db.SaveChanges();
// 1
outputDelegate(“AbstractMaster.AbstractDetails”,AbstractMaster.AbstractDetails);
outputDelegate(“ConcreteMaster.ConcreteDetails”,ConcreteMaster.ConcreteDetails);
//从数据库集中删除ConcreteDetail的wa将删除Id==1
db.ConcreteDetails.Remove(concreteMaster.ConcreteDetails.Single(cd=>cd.Id==1));
db.SaveChanges();
//
outputDelegate(“AbstractMaster.AbstractDetails”,AbstractMaster.AbstractDetails);
outputDelegate(“ConcreteMaster.ConcreteDetails”,ConcreteMaster.ConcreteDetails);
}
var input=Console.ReadLine();
}
}
class Program
{
static void Main(string[] args)
{
using( var db = new TestEntities() )
{
var master = new ConcreteMaster();
var details = new[]{
new ConcreteDetail() { Id = 1 },
new ConcreteDetail() { Id = 2 },
new ConcreteDetail() { Id = 3 },
new ConcreteDetail() { Id = 4 }
};
master.AbstractDetails.Add( details[ 0 ] );
master.ConcreteDetails.Add( details[ 1 ] );
details[ 2 ].AbstractMaster = master;
details[ 3 ].ConcreteMaster = master;
db.ConcreteMasters.Add( master );
db.AbstractDetails.Add( details[ 2 ] );
db.ConcreteDetails.Add( details[ 3 ] );
db.SaveChanges();
}
using( var db = new TestEntities() )
{
var concreteMaster = db.ConcreteMasters.Single();
var abstractMaster = db.AbstractMasters.Single();
Action<string, IEnumerable<AbstractDetail>> outputDelegate = ( string header, IEnumerable<AbstractDetail> details ) =>
{
if( details.Count() > 0 )
{
Console.WriteLine( "{0}: {1}", header, string.Join( ", ", details.Select( ad => ad.Id.ToString() ) ) );
}
else
{
Console.WriteLine( "{0}: <empty>", header );
}
};
// 1, 2, 3, 4
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
// remove Id == 4 by way of removing from abstract collection
abstractMaster.AbstractDetails.Remove( abstractMaster.AbstractDetails.Single( ad => ad.Id == 4 ) );
db.SaveChanges();
// 1, 2, 3
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
// remove Id == 3 by way of removing from concrete collection
concreteMaster.ConcreteDetails.Remove( concreteMaster.ConcreteDetails.Single( cd => cd.Id == 3 ) );
db.SaveChanges();
// 1, 2
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
// remove Id == 2 by way of removing AbstractDetail from DbSet<AbstractDetail>
db.AbstractDetails.Remove( abstractMaster.AbstractDetails.Single( ad => ad.Id == 2 ) );
db.SaveChanges();
// 1
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
// remove Id == 1 by wa of removing ConcreteDetail from DbSet<ConcreteDetail>
db.ConcreteDetails.Remove( concreteMaster.ConcreteDetails.Single( cd => cd.Id == 1 ) );
db.SaveChanges();
// <empty>
outputDelegate( "AbstractMaster.AbstractDetails", abstractMaster.AbstractDetails );
outputDelegate( "ConcreteMaster.ConcreteDetails", concreteMaster.ConcreteDetails );
}
var input = Console.ReadLine();
}
}