Использование 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
недвижимость в инкубаторе в 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();
}
}