Wpf NHibernate和奇怪的铸造例外
第二天我就要和它战斗了,我受够了 我的UI出现了奇怪的异常 第一件事。 我的模型基本上是这样的:Wpf NHibernate和奇怪的铸造例外,wpf,data-binding,nhibernate,Wpf,Data Binding,Nhibernate,第二天我就要和它战斗了,我受够了 我的UI出现了奇怪的异常 第一件事。 我的模型基本上是这样的: - Document - Period 1 - Sheet 1 基类: public class DbItem: ObservableModel { public virtual Document ParentDocument { get; set; } Guid id; public virtual Guid Id { get { r
- Document
- Period 1
- Sheet 1
基类:
public class DbItem: ObservableModel
{
public virtual Document ParentDocument { get; set; }
Guid id;
public virtual Guid Id
{
get { return id; }
set
{
if (id != value)
{
id = value;
NotifyPropertyChanged();
}
}
}
string name = string.Empty;
public virtual string Name
{
get { return name; }
set
{
if (value == null || name != value)
{
name = value;
NotifyPropertyChanged();
}
}
}
}
public enum PeriodType
{
Year,
Sheet
}
public abstract class PeriodBase : DbItem
{
public virtual Period ParentPeriod { get; set; }
public virtual PeriodType PeriodType { get; set; }
}
接下来是基类:
public class DbItem: ObservableModel
{
public virtual Document ParentDocument { get; set; }
Guid id;
public virtual Guid Id
{
get { return id; }
set
{
if (id != value)
{
id = value;
NotifyPropertyChanged();
}
}
}
string name = string.Empty;
public virtual string Name
{
get { return name; }
set
{
if (value == null || name != value)
{
name = value;
NotifyPropertyChanged();
}
}
}
}
public enum PeriodType
{
Year,
Sheet
}
public abstract class PeriodBase : DbItem
{
public virtual Period ParentPeriod { get; set; }
public virtual PeriodType PeriodType { get; set; }
}
还有一些属性,但为了清晰起见,我只是在这里删除了它们
接下来,我们有一个从PeriodBase继承的Period类:
public class Period : PeriodBase
{
IList<PeriodBase> periods = new ObservableCollection<PeriodBase>();
public virtual IList<PeriodBase> Periods
{
get { return periods; }
set
{
if (periods != value)
{
periods = value;
NotifyPropertyChanged();
}
}
}
}
public class Sheet : PeriodBase
{
DateTimeOffset startDate;
public override DateTimeOffset StartDate
{
get { return startDate; }
set
{
if (startDate != value)
{
startDate = value;
NotifyPropertyChanged();
}
}
}
DateTimeOffset endDate;
public override DateTimeOffset EndDate
{
get { return endDate; }
set
{
if (endDate != value)
{
endDate = value;
NotifyPropertyChanged();
}
}
}
}
最后是document类,它由句点组成:
public class Document: DbItem
{
IList<Period> periods = new ObservableCollection<Period>();
public virtual IList<Period> Periods
{
get { return periods; }
set
{
if (periods != value)
{
periods = value;
NotifyPropertyChanged();
}
}
}
}
我的绑定如下所示:
public class DocumentMap : DbItemMap<Document>
{
public DocumentMap()
{
Table("documents");
HasMany(x => x.Periods).ForeignKeyConstraintName("ParentDocument_id");
}
}
public class PeriodBaseMap: DbItemMap<PeriodBase>
{
public PeriodBaseMap()
{
UseUnionSubclassForInheritanceMapping();
References(x => x.ParentPeriod);
Map(x => x.Name).Not.Nullable();
Map(x => x.PeriodType).CustomType<PeriodType>();
}
}
public class PeriodMap : SubclassMap<Period>
{
public PeriodMap()
{
Table("periods");
Abstract();
References(x => x.ParentDocument);
HasMany(x => x.Periods).Inverse().Not.LazyLoad();
}
}
public class SheetMap : SubclassMap<Sheet>
{
public SheetMap()
{
Table("sheets");
Abstract();
Map(x => x.StartDate);
Map(x => x.EndDate);
}
}
重要的是,在启动应用程序并加载文档并单击treeview中的扩展器以展开期间后,我会遇到相同的错误。但是当我第一次运行应用程序时,一切都正常,直到我保存了文档
有什么问题吗
回复马克·费尔德曼的帖子
我决定回答,因为这太长了,无法评论。这是我第一次见到ORM,所以我可能对此有一些错误的想法。我的解决方案中只有一个模型。通常(使用SQL)它会工作。我将获取一个对象,将其插入到DB中,另一种方法也是如此
所以我在这里也是这么做的。我只有一个商业模式,它有一些简单的商业规则。它在ViewModels中使用,并存储在db中。这是一个糟糕的解决方案吗?我应该有另一个模式和一些打破干涸的原则
在我的脑海中,假设它是这样工作的:用户单击“创建新工作表”。这是(这是从命令调用的我的ViewModel->方法的一部分):
ModifiedItems只是一个保存已修改项的字典。由于这一点,我不必保存整个文档,只需修改项目即可
据我所知,这不是应该的。那么正确的方法是什么呢?或者ORM不适合这里?除非在我使用它之后的几年中NHibernate发生了重大变化,否则您不能仅仅从ObservaleModel派生模型类并期望它工作。看来,您的理由是将INPC赋予DB模型,有些人会认为这不是很好的关注点分离,并表明您的视图模型层设计不正确 也就是说,如果你真的坚持要这样做,那么不要从ObservaleModel中派生实体,而是尝试在NHibernate首次创建实体时使用Castle Dynamic Proxy之类的工具将INPC注入实体。Ayende Rahien的帖子展示了如何做到这一点,并提供了您需要的代码 您将面临的下一个问题是集合问题。同样,您不能只将
observateCollection
分配给IList
属性并期望它工作,NHibernate在反序列化集合时会替换整个列表,而不是对已分配的现有集合使用“添加/删除”。加载列表后,可以使用ObserveableCollection
替换列表,但如果这样做,则无论是否更改,NHibernate都会认为整个列表已更改,并再次序列化整个列表。一开始你会侥幸逃脱的,但很快你的表演就会开始受到伤害
要解决这个问题,您必须使用约定,以便NHibernate创建支持INotifyCollectionChanged的集合实体。不幸的是,我最初读到的关于这一点的页面早就消失了,所以我只能在这里发布代码(遗憾的是没有归属)。我只在NHibernate Fluent中使用了约定,所以我将让您了解如何在您自己的案例中应用它们,但以下是您需要的
public class ObservableBagConvention : ICollectionConvention
{
public void Apply(ICollectionInstance instance)
{
Type collectionType = typeof(ObservableBagType<>)
.MakeGenericType(instance.ChildType);
instance.CollectionType(collectionType);
instance.LazyLoad();
}
}
public class ObservableBagType<T> : CollectionType, IUserCollectionType
{
public ObservableBagType(string role, string foreignKeyPropertyName, bool isEmbeddedInXML)
: base(role, foreignKeyPropertyName, isEmbeddedInXML)
{
}
public ObservableBagType()
: base(string.Empty, string.Empty, false)
{
}
public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
{
return new PersistentObservableGenericBag<T>(session);
}
public override IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister, object key)
{
return new PersistentObservableGenericBag<T>(session);
}
public override IPersistentCollection Wrap(ISessionImplementor session, object collection)
{
return new PersistentObservableGenericBag<T>(session, (ICollection<T>)collection);
}
public IEnumerable GetElements(object collection)
{
return ((IEnumerable)collection);
}
public bool Contains(object collection, object entity)
{
return ((ICollection<T>)collection).Contains((T)entity);
}
protected override void Clear(object collection)
{
((IList)collection).Clear();
}
public object ReplaceElements(object original, object target, ICollectionPersister persister, object owner, IDictionary copyCache, ISessionImplementor session)
{
var result = (ICollection<T>)target;
result.Clear();
foreach (var item in ((IEnumerable)original))
{
if (copyCache.Contains(item))
result.Add((T)copyCache[item]);
else
result.Add((T)item);
}
return result;
}
public override object Instantiate(int anticipatedSize)
{
return new ObservableCollection<T>();
}
public override Type ReturnedClass
{
get
{
return typeof(PersistentObservableGenericBag<T>);
}
}
}
公共类observebagconvention:ICollectionConvention
{
public void Apply(ICollectionInstance实例)
{
类型collectionType=typeof(ObservableBagType)
.MakeGenericType(实例.ChildType);
实例.CollectionType(CollectionType);
instance.LazyLoad();
}
}
公共类observebagtype:CollectionType、IUserCollectionType
{
公共ObservableBagType(字符串角色、字符串foreignKeyPropertyName、布尔isEmbeddedInXML)
:base(角色、foreignKeyPropertyName、isEmbeddedInXML)
{
}
public observebagtype()
:base(string.Empty,string.Empty,false)
{
}
公共IPersistentCollection实例化(ISessionImplementor会话,ICollectionPersister persister)
{
返回新的PersistentObservableGenericBag(会话);
}
公共重写IPersistentCollection实例化(ISessionImplementor会话、ICollectionPersister持久化器、对象键)
{
返回新的PersistentObservableGenericBag(会话);
}
公共覆盖IPersistentCollection包装(ISessionImplementor会话,对象集合)
{
返回新的PersistentObservableGenericBag(会话,(ICollection)集合);
}
公共IEnumerable GetElements(对象集合)
{
返回((IEnumerable)集合);
}
公共布尔包含(对象集合、对象实体)
{
返回((ICollection)集合)。包含((T)实体);
}
受保护的替代无效清除(对象集合)
{
((IList)集合)。清除();
}
公共对象替换元素(对象原始、对象目标、ICollectionPersister持久器、对象所有者、IDictionary copyCache、ISessionImplementor会话)
{
var结果=(ICollection)目标;
result.Clear();
foreach(原始(IEnumerable)中的var项目)
{
if(copyCache.Contains(项))
添加((T)copyCache[项]);
其他的
结果。添加((T)项);
}
返回结果;
}
公共重写对象实例化(int expectedsize)
{
返回新的ObservableCollection();
}
公共重写类型ReturnedClass
{
得到
{
返回
async Task SaveDocument(Document doc)
{
foreach(var item in doc.ModifiedItems)
db.SaveOrUpdate(item);
}
public class ObservableBagConvention : ICollectionConvention
{
public void Apply(ICollectionInstance instance)
{
Type collectionType = typeof(ObservableBagType<>)
.MakeGenericType(instance.ChildType);
instance.CollectionType(collectionType);
instance.LazyLoad();
}
}
public class ObservableBagType<T> : CollectionType, IUserCollectionType
{
public ObservableBagType(string role, string foreignKeyPropertyName, bool isEmbeddedInXML)
: base(role, foreignKeyPropertyName, isEmbeddedInXML)
{
}
public ObservableBagType()
: base(string.Empty, string.Empty, false)
{
}
public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
{
return new PersistentObservableGenericBag<T>(session);
}
public override IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister, object key)
{
return new PersistentObservableGenericBag<T>(session);
}
public override IPersistentCollection Wrap(ISessionImplementor session, object collection)
{
return new PersistentObservableGenericBag<T>(session, (ICollection<T>)collection);
}
public IEnumerable GetElements(object collection)
{
return ((IEnumerable)collection);
}
public bool Contains(object collection, object entity)
{
return ((ICollection<T>)collection).Contains((T)entity);
}
protected override void Clear(object collection)
{
((IList)collection).Clear();
}
public object ReplaceElements(object original, object target, ICollectionPersister persister, object owner, IDictionary copyCache, ISessionImplementor session)
{
var result = (ICollection<T>)target;
result.Clear();
foreach (var item in ((IEnumerable)original))
{
if (copyCache.Contains(item))
result.Add((T)copyCache[item]);
else
result.Add((T)item);
}
return result;
}
public override object Instantiate(int anticipatedSize)
{
return new ObservableCollection<T>();
}
public override Type ReturnedClass
{
get
{
return typeof(PersistentObservableGenericBag<T>);
}
}
}
public class PersistentObservableGenericBag<T> : PersistentGenericBag<T>, INotifyCollectionChanged,
INotifyPropertyChanged, IList<T>
{
private NotifyCollectionChangedEventHandler _collectionChanged;
private PropertyChangedEventHandler _propertyChanged;
public PersistentObservableGenericBag(ISessionImplementor sessionImplementor)
: base(sessionImplementor)
{
}
public PersistentObservableGenericBag(ISessionImplementor sessionImplementor, ICollection<T> coll)
: base(sessionImplementor, coll)
{
CaptureEventHandlers(coll);
}
public PersistentObservableGenericBag()
{
}
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add
{
Initialize(false);
_collectionChanged += value;
}
remove { _collectionChanged -= value; }
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged
{
add
{
Initialize(false);
_propertyChanged += value;
}
remove { _propertyChanged += value; }
}
#endregion
public override void BeforeInitialize(ICollectionPersister persister, int anticipatedSize)
{
base.BeforeInitialize(persister, anticipatedSize);
CaptureEventHandlers(InternalBag);
}
private void CaptureEventHandlers(ICollection<T> coll)
{
var notificableCollection = coll as INotifyCollectionChanged;
var propertyNotificableColl = coll as INotifyPropertyChanged;
if (notificableCollection != null)
notificableCollection.CollectionChanged += OnCollectionChanged;
if (propertyNotificableColl != null)
propertyNotificableColl.PropertyChanged += OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler changed = _propertyChanged;
if (changed != null) changed(this, e);
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler changed = _collectionChanged;
if (changed != null) changed(this, e);
}
}