9

Registration在and之间的单向多对多关系中Item,其中 aRegistration有一个ISet<Item> ItemsPurchased并且Item没有返回注册的引用(这不是探索对象图的有用方法),当我查看正在生成的 SQL 时,我看到

INSERT INTO Registrations_Items (RegistrationId, ItemId) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 1 [Type: Int32 (0)]
UPDATE Items SET Price = @p0, Name = @p1, [...], ListIndex = @p5, EventId = @p6 WHERE ItemId = @p7

传递给更新的参数是正确的,但是关于 Item 没有任何改变,因此不需要更新。

映射是通过使用此覆盖进行自动映射,Registration而没有覆盖Item. DB Schema 看起来完全正确。我删除了所有约定并再次测试并且行为仍然存在,因此不是我的任何映射约定都在这样做。

mapping.HasManyToMany(e => e.ItemsPurchased).AsSet().Cascade.All().Not.Inverse();

为什么 NHibernate 会打这个UPDATE电话,我能做些什么来阻止它?这并没有真正伤害任何东西,但它表明我做错了什么,所以我想弄清楚是什么。

编辑: 根据下面的评论,我创建了一个单元测试,它创建一个EventItem必须属于一个Event),向其中添加两个Items,从会话中驱逐第一个并刷新会话,然后通过其 ID 获取第一个。

我注意到下面的 SELECT 项目行中有一些奇怪的东西(倒数第二个)

INSERT INTO Events (blah blah blah...)
select @@IDENTITY
INSERT INTO Items (Price, Name, StartDate, EndDate, ExternalID, ListIndex, EventId) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6);@p0 = 100.42 [Type: Decimal (0)], @p1 = 'Item 1' [Type: String (0)], @p2 = NULL [Type: DateTime (0)], @p3 = NULL [Type: DateTime (0)], @p4 = '123' [Type: String (0)], @p5 = 0 [Type: Int32 (0)], @p6 = 1 [Type: Int32 (0)]
select @@IDENTITY
SELECT blah blah blah FROM Events event0_ WHERE event0_.EventId=@p0;@p0 = 1 [Type: Int32 (0)]
SELECT itemsforsa0_.EventId as EventId1_, itemsforsa0_.ItemId as ItemId1_, itemsforsa0_.ListIndex as ListIndex1_, itemsforsa0_.ItemId as ItemId3_0_, itemsforsa0_.Price as Price3_0_, itemsforsa0_.Name as Name3_0_, itemsforsa0_.StartDate as StartDate3_0_, itemsforsa0_.EndDate as EndDate3_0_, itemsforsa0_.ExternalID as ExternalID3_0_, itemsforsa0_.ListIndex as ListIndex3_0_, itemsforsa0_.EventId as EventId3_0_ FROM Items itemsforsa0_ WHERE itemsforsa0_.EventId=@p0;@p0 = 1 [Type: Int32 (0)]
UPDATE Items SET Price = @p0, Name = @p1, StartDate = @p2, EndDate = @p3, ExternalID = @p4, ListIndex = @p5, EventId = @p6 WHERE ItemId = @p7;@p0 = 100.42000 [Type: Decimal (0)], @p1 = 'Item 1' [Type: String (0)], @p2 = NULL [Type: DateTime (0)], @p3 = NULL [Type: DateTime (0)], @p4 = '123' [Type: String (0)], @p5 = 0 [Type: Int32 (0)], @p6 = 1 [Type: Int32 (0)], @p7 = 1 [Type: Int32 (0)]

该表已正确创建:

create table Items (
    ItemId INT IDENTITY NOT NULL,
   Price NUMERIC(19,5) not null,
   Name NVARCHAR(255) not null,
   StartDate DATETIME null,
   EndDate DATETIME null,
   ExternalID NVARCHAR(255) not null,
   ListIndex INT not null,
   EventId INT not null,
   primary key (ItemId)
)

DateTimes 是故意可以为空的,因为项目可能不需要特定于日期(例如“早鸟注册”)。

4

2 回答 2

10

这称为:Phantom Updates,它通常与您的对象的映射有关

这是主要原因:

想象一下我们有一个像这样的对象

public class Product
{
   public Guid Id { get; set; }
   public int ReorderLevel { get; set; }
   public decimal UnitPrice { get; set; }
}

和一张地图:

public class ProductMap : ClassMap<Product>
{
   public ProductMap()
   {
      Not.LazyLoad();
      Id(x => x.Id).GeneratedBy.GuidComb();
      Map(x => x.ReorderLevel);
      Map(x => x.UnitPrice).Not.Nullable();
   }
}

请注意,ReorderLevel将接受空值

如果您在不指定 a 的情况下保存此实体ReorderLevel,它将与一个null值一起保存,但是当您从数据库加载它时,由于ReorderLevel类型为int,将添加一个0,这将导致实体被标记为脏,因此它将导致更新

这类错误很难检测和跟踪,我建议您Nullable<>在数据库中确实需要 null 时使用类型

我通常完成此操作的方法是创建一个约定,如果它们被声明,它将自动将 my 设置Value Types为,否则该字段将被标记为nullNullable<>NotNullable

作为补充,这就是我的约定的样子:

    mapper.BeforeMapProperty += (ins, memb, cust) =>
    {
        var type = memb.LocalMember.GetPropertyOrFieldType();

        if (type.IsValueType)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                cust.Column(x => { x.NotNullable(notnull: false); });
            }
            else
            {
                cust.Column(x => { x.NotNullable(notnull: true); });
            }
        }
    }
于 2012-05-30T20:43:11.633 回答
1

如上所述(如下?谁知道。寻找​​我在另一个答案上留下的评论),我注意到CanGenerateDatabaseSchema单元测试和CanGetItem单元测试之间的区别在于一个给我DECIMAL (6,2),另一个给我DECIMAL (19,0)

我摸索了更多,意识到这CanGenerateDatabaseSchema是使用我的“真实”配置(来自 Web 项目),而另一个测试正在使用我的“单元测试”配置。我的单元测试是针对 Sql Server CE 运行的......当我将单元测试更改为使用与我的真实数据库(Sql Server 2005)相同的配置时,幻象更新突然消失了。

因此,如果其他人遇到带有小数的意外幻像更新...请检查您是否使用的是 Sql Server CE。由于测试实际上通过了(说它失败的评论是不正确的,它没有失败,只是做额外的工作),我想我会接受它,尽管为什么 Sql CE 忽略我的配置是一个很好的问题,并且可能的 NH 或 FNH 错误。

于 2012-05-31T15:00:51.033 回答