我正在使用Entity Framework 6代码优先编写DAL。我有一个每表类型的继承结构。
我有四个类:AbstractMaster,ConcreteMaster,AbstractDetail和ConcreteDetail。直观地说,具体类继承自抽象类,并且抽象和具体的主和细节之间存在一对多的关系。每种类型的表格是一种要求。
如果我将ConcreteDetail添加到ConcreteMaster实体并保存更改(DbContext),则会收到外键错误。原因是已经设置了从ConcreteDetail到ConcreteMaster的反向引用,但是尚未设置从AbstractDetail到AbstractMaster的反向引用。
如果我在'摘要'中删除了一对多的关系。等级,然后我的测试通过。虽然数据完整性仍然在“具体”的情况下得到执行。级别,数据库仍然缺少合法的外键。看起来像一个有效的用例?
有什么建议吗?
谢谢, 约翰
答案 0 :(得分:0)
将ObservableCollection<TEntity>
用于主类的集合导航属性。为CollectionChanged
类中的每个集合的ConcreteMaster
事件实现处理程序方法。这里的想法是当一个项目被添加或移除到一个集合时,您将添加/删除其他集合中的相同项目:
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>();
}
}
ConcreteMaster类:
[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 );
}
}
}
}
在详细信息方面,为INotifyPropertyChanged
实施AbstractDetail
接口,在AbstractMaster
属性发生更改时引发事件:
[Table( "AbstractDetail" )]
public abstract class AbstractDetail : INotifyPropertyChanged
{
public int Id { get; set; }
private AbstractMaster _abstractMaster = null;
public AbstractMaster AbstractMaster
{
get
{
return _abstractMaster;
}
set
{
if( value != _abstractMaster )
{
_abstractMaster = value;
if( null != PropertyChanged )
{
PropertyChanged( this, new PropertyChangedEventArgs( "AbstractMaster" ) );
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
最后,在ConcreteDetail
类中,在base.AbstractMaster
属性的setter中设置ConcreteMaster
属性,并向base.AbstractMaster
添加一个将更新this.ConcreteMaster
的事件处理程序当base.AbstractMaster
发生变化时:
[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();
}
}