我有以下测试支持类。
public class FixtureBase
{
protected SessionSource SessionSource { get; set; }
protected ISession Session { get; private set; }
[TestFixtureSetUp]
public void SetupFixture()
{
var cfg = Fluently.Configure().Database(SQLiteConfiguration.Standard.ShowSql().InMemory);
SessionSource = new SessionSource(cfg.BuildConfiguration().Properties, new TestModel());
}
[SetUp]
public void SetupContext()
{
Session = SessionSource.CreateSession();
SessionSource.BuildSchema(Session);
}
[TearDown]
public void TearDownContext()
{
Session.Close();
Session.Dispose();
}
}
public TestModel()
{
this.AddMappingsFromAssembly(typeof(StudentMap).Assembly);
}
一个非常简单的测试类,它应该以多对多的关系测试链接类映射。
[TestFixture]
public class StudentGuardianAssociationMap_Fixture : FixtureBase
{
[Test]
public void can_correctly_map_studentguardianassociation()
{
Guardian guardian = new Guardian
{
FirstName = "GuardianFName",
LastName = "GuardianLName",
NameSuffix = "GuardianSuffix",
MiddleName = "GuardianMiddleName"
};
Student student = new Student
{
FirstName = "StudentFName",
LastName = "StudentLName",
MiddleName = "StudentMiddleName",
Address1 = "StudentAddress1",
Address2 = "StudentAddress2",
City = "StudentCity",
State = "MO",
PostalCode = "12345-2342"
};
new PersistenceSpecification<StudentGuardianAssociation>(Session)
.CheckProperty(x => x.RelationShipToStudent, 1)
.CheckReference(x => x.AssociatedStudent, student)
.CheckReference(x => x.AssociatedGuardian, guardian)
.VerifyTheMappings();
}
}
运行此测试时,我收到以下异常。
System.ApplicationException:For 财产“AssociatedStudent”预计 'Pats.DataTransfer.Student'的类型 'Pats.DataTransfer.Student'但得到了'' 类型'Pats.DataTransfer.Student'
一些研究表明,这种错误通常会发生,因为当您未能在DTO中覆盖对象相等时,我目前正在基于我的DTO基类中的id实现非常基本的对象相等。
public class DataTranfserObject<TDto> where TDto : DataTranfserObject<TDto>
{
private int? _oldHashCode;
public virtual int Id { get; set; }
public virtual int Version { get; set; }
public override int GetHashCode()
{
// Once we have a hash code we'll never change it
if (_oldHashCode.HasValue)
{
return _oldHashCode.Value;
}
var thisIsTransient = Equals(Id, 0);
// When this instance is transient, we use the base GetHashCode()
// and remember it, so an instance can NEVER change its hash code.
if (thisIsTransient)
{
_oldHashCode = base.GetHashCode();
return _oldHashCode.Value;
}
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as TDto;
if (other == null)
{
return false;
}
// handle the case of comparing two NEW objects
var otherIsTransient = Equals(other.Id, 0);
var thisIsTransient = Equals(Id, 0);
if (otherIsTransient && thisIsTransient)
{
return ReferenceEquals(other, this);
}
return other.Id.Equals(Id);
}
}
我的映射表的对象表示。
public class StudentGuardianAssociation : DataTranfserObject<StudentGuardianAssociation>
{
public virtual int RelationShipToStudent { get; set; }
public virtual Student AssociatedStudent { get; set; }
public virtual Guardian AssociatedGuardian { get; set; }
}
最后我的地图很好。
public StudentGuardianAssociationMap()
{
LazyLoad();
this.Table("StudentGuardians");
this.Id(x => x.Id).GeneratedBy.HiLo("100").UnsavedValue(0);
this.Version(x => x.Version).Column("Version");
Map(x => x.RelationShipToStudent).Column("RelationshipToStudent").Not.Nullable();
References(x => x.AssociatedGuardian).Not.Nullable();
References(x => x.AssociatedStudent).Not.Nullable();
}
我对nhibernate和流畅的api仍然很陌生,但我已经成功地让我的学生和监护人地图通过测试。虽然我还没有为他们的HasMany Associations部分包含测试。
底线,是什么导致
System.ApplicationException:For 财产“AssociatedStudent”预计 'Pats.DataTransfer.Student'的类型 'Pats.DataTransfer.Student'但得到了'' 类型'Pats.DataTransfer.Student'
我的测试期间会抛出异常,我应采取什么策略来纠正它。
这是nunit返回的堆栈跟踪信息。
at FluentNHibernate.Testing.Values.Property`2.CheckValue(Object target) in d:\Builds\FluentNH\src\FluentNHibernate\Testing\Values\Property.cs: line 91 at FluentNHibernate.Testing.PersistenceSpecification`1.c__DisplayClass2.b__1(Property`1 p) in d:\Builds\FluentNH\src\FluentNHibernate\Testing\PersistenceSpecification.cs: line 64 at System.Collections.Generic.List`1.ForEach(Action`1 action) at FluentNHibernate.Testing.PersistenceSpecification`1.VerifyTheMappings(T first) in d:\Builds\FluentNH\src\FluentNHibernate\Testing\PersistenceSpecification.cs: line 64 at FluentNHibernate.Testing.PersistenceSpecification`1.VerifyTheMappings() in d:\Builds\FluentNH\src\FluentNHibernate\Testing\PersistenceSpecification.cs: line 40 at Pats.DataAccessTest.StudentGuardianAssociationMap_Fixture.can_correctly_map_studentguardianassociation() in StudentGuardianAssociationMap_Fixture.cs: line 37
如果有任何见解,则执行sql。
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 2, @p1 = 1
NHibernate: INSERT INTO Students (Version, FirstName, LastName, MiddleName, NameSuffix, Address1, Address2, City, State, PostalCode, Id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10);@p0 = 1, @p1 = 'StudentFName', @p2 = 'StudentLName', @p3 = 'StudentMiddleName', @p4 = NULL, @p5 = 'StudentAddress1', @p6 = 'StudentAddress2', @p7 = 'StudentCity', @p8 = 'MO', @p9 = '12345-2342', @p10 = 1001
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 3, @p1 = 2
NHibernate: INSERT INTO Guardians (Version, FirstName, LastName, MiddleName, NameSuffix, Id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5);@p0 = 1, @p1 = 'GuardianFName', @p2 = 'GuardianLName', @p3 = 'GuardianMiddleName', @p4 = 'GuardianSuffix', @p5 = 2002
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 4, @p1 = 3
NHibernate: INSERT INTO StudentGuardians (Version, RelationshipToStudent, Id) VALUES (@p0, @p1, @p2);@p0 = 1, @p1 = 1, @p2 = 3003
NHibernate: SELECT studentgua0_.Id as Id1_2_, studentgua0_.Version as Version1_2_, studentgua0_.RelationshipToStudent as Relation3_1_2_, student1_.Id as Id2_0_, student1_.Version as Version2_0_, student1_.FirstName as FirstName2_0_, student1_.LastName as LastName2_0_, student1_.MiddleName as MiddleName2_0_, student1_.NameSuffix as NameSuffix2_0_, student1_.Address1 as Address7_2_0_, student1_.Address2 as Address8_2_0_, student1_.City as City2_0_, student1_.State as State2_0_, student1_.PostalCode as PostalCode2_0_, guardian2_.Id as Id0_1_, guardian2_.Version as Version0_1_, guardian2_.FirstName as FirstName0_1_, guardian2_.LastName as LastName0_1_, guardian2_.MiddleName as MiddleName0_1_, guardian2_.NameSuffix as NameSuffix0_1_ FROM StudentGuardians studentgua0_ left outer join Students student1_ on studentgua0_.Id=student1_.Id left outer join Guardians guardian2_ on studentgua0_.Id=guardian2_.Id WHERE studentgua0_.Id=@p0;@p0 = 3003
感谢您浏览我的文字墙,我只是想彻底了解我想要完成的事情,以及到目前为止我所做的事情。
答案 0 :(得分:2)
尝试使用重载的PersistenceSpecification构造函数,该构造函数使用IEqualityComparer来比较子对象(AssociatedGuardian,AssociatedStudent)。持久性规范测试比较两个不同的引用(原始和检索),因此它们将与您的实现具有不同的哈希码。据我了解,使用IEqualityComparer首次比较第一次比较哈希码,然后检查等于哈希码是否匹配。我的猜测是PersistenceSpecification在IEqualityComparer实现中包装了Equals调用。
我使用实用程序类来简化这一过程:
public class PersistenceSpecificationEqualityComparer : IEqualityComparer
{
private readonly Dictionary<Type, Delegate> _comparers = new Dictionary<Type, Delegate>();
public void RegisterComparer<T>(Func<T, object> comparer)
{
_comparers.Add(typeof(T), comparer);
}
public bool Equals(object x, object y)
{
if (x == null || y == null)
{
return false;
}
var xType = x.GetType();
var yType = y.GetType();
// check subclass to handle proxies
if (_comparers.ContainsKey(xType) && (xType == yType || yType.IsSubclassOf(xType)))
{
var comparer = _comparers[xType];
var xValue = comparer.DynamicInvoke(new[] {x});
var yValue = comparer.DynamicInvoke(new[] {y});
return xValue.Equals(yValue);
}
return x.Equals(y);
}
public int GetHashCode(object obj)
{
throw new NotImplementedException();
}
}
用法:
var comparer = new PersistenceSpecificationEqualityComparer();
comparer.RegisterComparer((Guardian x) => x.Id);
comparer.RegisterComparer((Student x) => x.Id);
// etc.
new PersistenceSpecification<StudentGuardianAssociation>(Session, comparer)
.CheckProperty(x => x.RelationShipToStudent, 1)
.CheckReference(x => x.AssociatedStudent, student)
.CheckReference(x => x.AssociatedGuardian, guardian)
.VerifyTheMappings();
编辑:
我想我看到了问题:监护人和学生没有坚持在会话中,并且没有自动执行的级联。在运行PersistenceSpecification之前保存这些对象应该修复它。您可以在this answer中查看CheckValue方法的代码。在再次查看您的问题时,错误消息指出“但得到''类型'Pats.DataTransfer.Student'”表示该值为空。
假设这是解决方案,我很好奇你是否还需要IEqualityComparer。使用诸如NHProf之类的工具进行性能分析会很快发现这一点,因为您会在插入中看到空值。
答案 1 :(得分:1)
我最终解决了这个问题,事实证明代码不是我的问题,而是使用了32位版本的System.Data.Sqlite.dll并将我的测试程序集编译为x86让它正常工作,我的其他程序集作为AnyCPU,在我的系统上意味着64位。出于某种原因,这适用于某些事情,但开始在CheckValue调用中表现出来,这让我有点失去了气味。
我能够为Sqlite找到一个64位的dll http://sqlite.phxsoftware.com/ 这很美妙。
现在生活很好,没有自定义的IEqualityComparer,或者在会话之前显式持久保存对象。