在执行单元测试之前,我使用NHibernate作为应用程序的DAL,并在particlular NHibernate的SchemaExport函数中删除/重新创建我的数据库模式。我遇到的问题是,当我运行单元测试并执行SchemaExport时,我的一个表每隔一次都无法删除。这会告诉我,有某种外键问题阻止了SchemaExport删除我的表--但是我不能确定。我的模式非常简单-一个person表、一个Address表和一个PersonAddress表来支持这两者之间的多对多关系。
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Address> Addresses {get;set;}
public Person()
{
this.Addresses = new List<Address>();
}
}
public class Address
{
public virtual int Id { get; set; }
public virtual string Street1 { get; set; }
public virtual string Street2 { get; set; }
public virtual string Postcode { get; set; }
}还有我的NHibernate映射文件。
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MyHibernate"
namespace="MyHibernate"
>
<class name="Person" table="Person.Person">
<id name="Id" column="Id">
<generator class="native" ></generator>
</id>
<property name="Name" column="Name" length="50"></property>
<bag name="Addresses" table="[Person].[PersonAddress]" lazy="false" cascade="all">
<key column="PersonId" foreign-key="FK_Person_Person_Id"></key>
<many-to-many class="Address" column="AddressId"></many-to-many>
</bag>
</class>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MyHibernate"
namespace="MyHibernate"
>
<class name="Address" table="Person.Address">
<id name="Id" column="Id">
<generator class="native" ></generator>
</id>
<property name="Street1" column="Street1" length="50"></property>
<property name="Street2" column="Street2" length="50"></property>
<property name="Postcode" column="Postcode" length="50"></property>
</class>
当我运行` `var cfg = new Configuration();cfg.Configure();cfg.AddAssembly(typeof(Person).Assembly);
new SchemaExport(cfg).Execute(false, true, false, false)我得到一个SQL异常,说:
MyHibernate.Tests.GenerateSchemaFixture.Can_Generate_Schema: NHibernate.HibernateException :数据库中已存在名为“Person”的对象。-> System.Data.SqlClient.SqlException :数据库中已存在名为Person的对象。
有什么想法吗?
发布于 2009-04-16 14:32:17
找到了解决这个问题的方法。我刚刚把SchemaExport的用法分成了两个调用。第一个删除现有的模式,第二个重新创建它。
var cfg = new Configuration();
cfg.Configure();
cfg.AddAssembly(typeof(Person).Assembly);
SchemaExport se = new SchemaExport(cfg);
//drop database
se.Drop(true, true);
//re-create database
se.Create(true, true);在我的测试类的TestFixtureSetUp中使用上面的代码效果很好。我现在有了一个干净的数据库模式,可以用于集成测试。
发布于 2011-06-30 22:28:18
很长一段时间以来,这一直是我反复遇到的问题。执行drop first或使用execute方法( drop方法是执行Execute方法的快捷方法)不能解决此问题。
在查看了NHibernate源代码之后,我找到了问题的根源。NHibernate使用哈希码在数据库中存储外键名称。然而,hashcode的问题是,它们会随着时间、clr版本和appdomain而变化。您不能依赖hashcode来实现等价性。(参考:http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)这就是为什么NHibernate不能总是删除外键,因此不能删除表。
这是NHibernate源码的快照,用于创建唯一的外键名称:
public string UniqueColumnString(IEnumerable iterator, string referencedEntityName)
{
// NH Different implementation (NH-1339)
int result = 37;
if (referencedEntityName != null)
{
result ^= referencedEntityName.GetHashCode();
}
foreach (object o in iterator)
{
result ^= o.GetHashCode();
}
return (name.GetHashCode().ToString("X") + result.GetHashCode().ToString("X"));
}所以这个问题不是NHibernate能解决的,你必须自己来解决。在创建模式之前,我通过执行以下方法解决了这个问题。该方法仅从使用NHibernate映射的表中删除所有外键:
private static void DropAllForeignKeysFromDatabase()
{
var tableNamesFromMappings = Configuration.ClassMappings.Select(x => x.Table.Name);
var dropAllForeignKeysSql =
@"
DECLARE @cmd nvarchar(1000)
DECLARE @fk_table_name nvarchar(1000)
DECLARE @fk_name nvarchar(1000)
DECLARE cursor_fkeys CURSOR FOR
SELECT OBJECT_NAME(fk.parent_object_id) AS fk_table_name,
fk.name as fk_name
FROM sys.foreign_keys fk JOIN
sys.tables tbl ON tbl.OBJECT_ID = fk.referenced_object_id
WHERE OBJECT_NAME(fk.parent_object_id) in ('" + String.Join("','", tableNamesFromMappings) + @"')
OPEN cursor_fkeys
FETCH NEXT FROM cursor_fkeys
INTO @fk_table_name, @fk_name
WHILE @@FETCH_STATUS=0
BEGIN
SET @cmd = 'ALTER TABLE [' + @fk_table_name + '] DROP CONSTRAINT [' + @fk_name + ']'
exec dbo.sp_executesql @cmd
FETCH NEXT FROM cursor_fkeys
INTO @fk_table_name, @fk_name
END
CLOSE cursor_fkeys
DEALLOCATE cursor_fkeys
;";
using (var connection = SessionFactory.OpenSession().Connection)
{
var command = connection.CreateCommand();
command.CommandText = dropAllForeignKeysSql;
command.ExecuteNonQuery();
}
}对我来说太完美了。我希望其他人也能使用它。这就是我获取sql脚本以删除所有外键的地方:http://mafudge.mysite.syr.edu/2010/05/07/dropping-all-the-foreign-keys-in-your-sql-server-database/
发布于 2009-10-18 13:37:39
我也收到了这个错误消息;然而,这是因为我删除了一个与另一个类具有多对一关系的类。因此,没有任何东西告诉NHibernate在下一次测试运行时删除现在已孤立但仍被引用的表。
我只是简单地用手把它扔掉,一切都很好。
https://stackoverflow.com/questions/730725
复制相似问题