NHibernate SchemaExport有时无法删除表....

Dav*_*ans 4 nhibernate nhibernate-mapping

我正在将NHibernate用于我的应用程序的DAL,并且在特定的NHibernate的SchemaExport函数中,在执行单元测试之前删除/重新创建我的数据库模式.我遇到的问题是,当我运行单元测试并执行SchemaExport时,我的一个表无法每秒都丢失.这将向我表明存在某种外键问题阻止SchemaExport放弃我的表 - 但我无法弄明白.我的架构非常简单 - 人员表,地址表和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; }

}
Run Code Online (Sandbox Code Playgroud)

和我的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>
Run Code Online (Sandbox Code Playgroud)

<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>
Run Code Online (Sandbox Code Playgroud)

当我运行`var cfg = new Configuration(); cfg.Configure(); cfg.AddAssembly(typeof运算(人).Assembly);

        new SchemaExport(cfg).Execute(false, true, false, false)
Run Code Online (Sandbox Code Playgroud)

我得到一个SQL异常说:

MyHibernate.Tests.GenerateSchemaFixture.Can_Generate_Schema:NHibernate.HibernateException:数据库中已经有一个名为"Person"的对象.----> System.Data.SqlClient.SqlException:数据库中已经有一个名为"Person"的对象.

有任何想法吗?

Rob*_*aap 12

这对我来说一直是一个反复出现的问题.首先执行drop或使用execute方法没有解决问题(drop方法是执行Execute方法的快捷方法).

在查看NHibernate源代码后,我找到了问题的根源.NHibernate使用哈希码将外键名存储在数据库中.然而,哈希码的问题在于它们会随着时间的推移而变化,clr-version和appdomain.您不能依赖哈希码来实现相等性.(参考: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"));
}
Run Code Online (Sandbox Code Playgroud)

所以这个问题不会被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();
    }
  }
Run Code Online (Sandbox Code Playgroud)

适合我的作品.我希望其他人也可以使用它.这是我抓住sql脚本删除所有外键的地方:http://mafudge.mysite.syr.edu/2010/05/07/dropping-all-the-foreign-keys-in-your-sql-server-数据库/


Dav*_*ans 9

找到解决这个问题的方法.我刚刚将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);
Run Code Online (Sandbox Code Playgroud)

在我的测试类的[TestFixtureSetUp]中使用上面的代码效果很好.我现在有一个干净的数据库模式用于集成测试.

  • `se.Drop(true,true);`是多余的,因为`se.Create(true,true);`已经**re** - 创建(读取**drop-create**)数据库 (3认同)