Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# NHibernate发出无关的update语句,而不考虑关系上的正确反向(流畅的NHibernate)设置_C#_Nhibernate_Fluent Nhibernate - Fatal编程技术网

C# NHibernate发出无关的update语句,而不考虑关系上的正确反向(流畅的NHibernate)设置

C# NHibernate发出无关的update语句,而不考虑关系上的正确反向(流畅的NHibernate)设置,c#,nhibernate,fluent-nhibernate,C#,Nhibernate,Fluent Nhibernate,下面的类以最简单的方式表示我使用遗留数据库的真实场景。我可以向其中添加新列,但这是我唯一能做的,因为300+100表数据库被许多其他遗留应用程序使用,这些应用程序不会被移植到NHibernate(因此,从复合键迁移不是一个选项): 以下是为前面代码生成的SQL语句: -- statement #1 INSERT INTO [Parent] DEFAULT VALUES; select SCOPE_IDENTITY() -- statement #2 INSERT INTO [Child]

下面的类以最简单的方式表示我使用遗留数据库的真实场景。我可以向其中添加新列,但这是我唯一能做的,因为300+100表数据库被许多其他遗留应用程序使用,这些应用程序不会被移植到NHibernate(因此,从复合键迁移不是一个选项):

以下是为前面代码生成的SQL语句:

-- statement #1
INSERT INTO [Parent]
DEFAULT VALUES;

select SCOPE_IDENTITY()

-- statement #2
INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_0 */,
             1 /* @p1_0 */,
             1 /* @p2_0 */)

INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_1 */,
             1 /* @p1_1 */,
             2 /* @p2_1 */)

INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_2 */,
             1 /* @p1_2 */,
             3 /* @p2_2 */)


-- statement #3
INSERT INTO [Item]
            (version,
             ItemId)
VALUES      (1 /* @p0_0 */,
             1 /* @p1_0 */)

-- statement #4
UPDATE [Child]
SET    version = 2 /* @p0 */
WHERE  PARENT_ID = 1 /* @p1 */
       AND CHILD_ID = 1 /* @p2 */
       AND version = 1 /* @p3 */

-- statement #5
UPDATE [Child]
SET    version = 2 /* @p0 */
WHERE  PARENT_ID = 1 /* @p1 */
       AND CHILD_ID = 2 /* @p2 */
       AND version = 1 /* @p3 */

-- statement #6
UPDATE [Child]
SET    version = 2 /* @p0 */
WHERE  PARENT_ID = 1 /* @p1 */
       AND CHILD_ID = 3 /* @p2 */
       AND version = 1 /* @p3 */

-- statement #7
UPDATE [Item]
SET    PARENT_ID = 1 /* @p0_0 */,
       CHILD_ID = 1 /* @p1_0 */
WHERE  ItemId = 1 /* @p2_0 */
语句4、5和6是无关的/多余的,因为所有这些信息都已在语句2中的批插入中发送到数据库

如果父映射没有在HasMany(一对多)关系上设置Inverse属性,这将是预期的行为

事实上,当我们摆脱从孩子到物品的一对多关系时,它变得更加奇怪,就像这样:

从子项中删除集合并将子属性添加到项中:

   public class Child
    {
        public virtual Parent Parent { get; set; }
        public virtual int ChildId { get; set; }
        long version;
        public override int GetHashCode() 
        {
            return ChildId.GetHashCode() ^ (Parent != null ? Parent.Id.GetHashCode() : 0.GetHashCode());
        }
        public override bool Equals(object obj)
         {
            var c = obj as Child;
            if (ReferenceEquals(c, null))
                return false;
            return ChildId == c.ChildId && Parent.Id == c.Parent.Id;
        }
    }

    public class Item
    {
        public virtual Child Child { get; set; }
        public virtual long ItemId { get; set; }
        long version;
    }
更改子项和项的映射,以从项中删除HasMany,并将项上的复合键上的引用添加回子项:

public class MapeamentoChild : ClassMap<Child>
{
    public MapeamentoChild()
    {
        CompositeId()
            .KeyReference(_ => _.Parent, "PARENT_ID")
            .KeyProperty(_ => _.ChildId, "CHILD_ID");
        Version(Reveal.Member<Child>("version"));
    }
}
public class MapeamentoItem : ClassMap<Item>
{
    public MapeamentoItem()
    {
        Id(_ => _.ItemId).GeneratedBy.Assigned();
        References(_ => _.Child).Columns("PARENT_ID", "CHILD_ID");
        Version(Reveal.Member<Item>("version"));
    }
}
生成的sql语句包括:

-- statement #1
INSERT INTO [Parent]
DEFAULT VALUES;

select SCOPE_IDENTITY()

-- statement #2
INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_0 */,
             1 /* @p1_0 */,
             1 /* @p2_0 */)

INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_1 */,
             1 /* @p1_1 */,
             2 /* @p2_1 */)

INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_2 */,
             1 /* @p1_2 */,
             3 /* @p2_2 */)

-- statement #3
INSERT INTO [Item]
            (version,
             PARENT_ID,
             CHILD_ID,
             ItemId)
VALUES      (1 /* @p0_0 */,
             1 /* @p1_0 */,
             1 /* @p2_0 */,
             1 /* @p3_0 */)
正如您所看到的,没有无关/多余的UPDATE语句,但对象模型不是自然建模的,因为我不希望项有一个指向Child的链接,并且我需要Child中的项集合

我找不到任何方法来阻止那些不想要的/不需要的UPDATE语句,除了从Child中删除任何HasMany关系。看来,既然孩子已经是“倒转”一对多关系中的“多”(它负责拯救自己),当它是另一对多倒转关系中的“一”部分时,它就不尊重倒转设置


这让我快发疯了。如果没有经过深思熟虑的解释,我无法接受这些额外的更新语句:-)有人知道这里发生了什么吗?

经过一整晚的努力,即使在堆栈溢出中也看不到答案:-)我想出了解决方案。。。我开始认为,可能是子对象中的更改被视为父对象集合中的更改,然后导致实体版本的更改。读了这篇文章后,我的猜测开始坚定:

(13) 乐观锁(可选-默认为true):该 对集合状态的更改将导致 拥有实体的版本。(对于一对多协会而言,通常 禁用此设置是合理的。) (可在此处找到:)

然后,我天真地更改了父对象上的映射,使其不使用乐观锁,如下所示:

    public MapeamentoParent()
    {
        Id(_ => _.Id, "PARENT_ID").GeneratedBy.Identity();
        HasMany<Child>(_ => _.Children)
            .Inverse()
            .AsSet()
            .Cascade.All()
            .Not.OptimisticLock()
            .KeyColumn("PARENT_ID");
    }
我很幸运地注意到版本被更新为2!(离题:我使用的是DateTime版本字段,但由于它的精度不是无限的,所以当我开始认为这是一个版本控制问题时,我故意将其更改为一个完整的版本,这样我就可以看到版本中的每一个增量,并且不会错过在不到毫秒的时间内发生的增量,而这些增量是无法通过日期跟踪的。)时间版本,因为它的精度,或缺乏)。因此,在再次绝望之前,我已将父对象的HasMany更改为以前的状态(尝试隔离任何可能的解决方案),并将Not.OPTIMITICLOCK()添加到子对象的映射中(在所有似乎更新了版本的实体都是子对象之后!):

没有任何无关的更新语句!!!:-)

问题是我仍然无法解释为什么它以前不起作用。出于某种原因,当子对象与另一个实体具有一对多关系时,会执行无关的SQL语句。必须在子对象上的这些一对多集合上将乐观锁设置为false。我也不知道为什么所有子对象的版本都同时更改,仅仅因为子类与添加到其中的项有一对多的关系。当只有一个子对象被更改时,增加所有子对象的版本号是没有意义的

我最大的问题是为什么父对象集合中的所有子对象都在更新,即使我没有向任何子对象添加任何项。这仅仅是因为孩子和物品有很多关系。。。(无需向任何子级添加任何项即可“获取”这些额外更新)。在我看来,NHibernate在这里把事情搞错了,但由于我对NHibernate完全缺乏更深入的了解,我不能肯定,也不能准确指出问题所在,甚至不能肯定这确实是一个问题,因为这很可能是我完全缺乏NHibernate的摸索能力——真正的罪魁祸首!:-)


我希望有更开明的人来解释发生了什么事情,但是按照文档的建议,将一对多关系的乐观锁设置为false解决了这个问题。

我发现parent.AddChild(…)和child.parent=parent之间存在差异;后者不会导致无关的版本更新(它不会在会话期间将子项添加到Parent.Children集合中,但在稍后的会话中加载父项时,它就在那里。)@BatteryBackupUnit这是真的,但作为ORM,它不应该“行为不检”将其添加到集合或直接设置父对象时,情况会有所不同。有时我们确实需要在从数据库加载它之前,立即将它放在集合中。在这些情况下,这些无关的疑问仍然存在。但很高兴知道,如果我们只想把孩子和父母“绑在一起”,我们就可以“阻止”他们。。。只需使用child.Parent,而不是添加到父集合中即可。:-)我完全同意。我已将FluentNHibernate
IHasManyConvention
.Not.OptimisticLock()
添加到我们的约定中,以确保这是默认行为。我花了半个上午跟踪了这一点。对我来说奇怪的是
public class MapeamentoChild : ClassMap<Child>
{
    public MapeamentoChild()
    {
        CompositeId()
            .KeyReference(_ => _.Parent, "PARENT_ID")
            .KeyProperty(_ => _.ChildId, "CHILD_ID");
        Version(Reveal.Member<Child>("version"));
    }
}
public class MapeamentoItem : ClassMap<Item>
{
    public MapeamentoItem()
    {
        Id(_ => _.ItemId).GeneratedBy.Assigned();
        References(_ => _.Child).Columns("PARENT_ID", "CHILD_ID");
        Version(Reveal.Member<Item>("version"));
    }
}
        using (var tx = session.BeginTransaction())
        {
            var parent = new Parent();
            var child = new Child() { ChildId = 1, };
            parent.AddChildren(
                child,
                new Child() { ChildId = 2, },
                new Child() { ChildId = 3 });
            var item = new Item() { ItemId = 1, Child = child };
            session.Save(parent);
            session.Save(item);
            tx.Commit();
        }
-- statement #1
INSERT INTO [Parent]
DEFAULT VALUES;

select SCOPE_IDENTITY()

-- statement #2
INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_0 */,
             1 /* @p1_0 */,
             1 /* @p2_0 */)

INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_1 */,
             1 /* @p1_1 */,
             2 /* @p2_1 */)

INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_2 */,
             1 /* @p1_2 */,
             3 /* @p2_2 */)

-- statement #3
INSERT INTO [Item]
            (version,
             PARENT_ID,
             CHILD_ID,
             ItemId)
VALUES      (1 /* @p0_0 */,
             1 /* @p1_0 */,
             1 /* @p2_0 */,
             1 /* @p3_0 */)
    public MapeamentoParent()
    {
        Id(_ => _.Id, "PARENT_ID").GeneratedBy.Identity();
        HasMany<Child>(_ => _.Children)
            .Inverse()
            .AsSet()
            .Cascade.All()
            .Not.OptimisticLock()
            .KeyColumn("PARENT_ID");
    }
-- statement #1
UPDATE [Child]
SET    version = 2 /* @p0 */
WHERE  PARENT_ID = 1 /* @p1 */
       AND CHILD_ID = 1 /* @p2 */
       AND version = 1 /* @p3 */

-- statement #2
UPDATE [Child]
SET    version = 2 /* @p0 */
WHERE  PARENT_ID = 1 /* @p1 */
       AND CHILD_ID = 2 /* @p2 */
       AND version = 1 /* @p3 */

-- statement #3
UPDATE [Child]
SET    version = 2 /* @p0 */
WHERE  PARENT_ID = 1 /* @p1 */
       AND CHILD_ID = 3 /* @p2 */
       AND version = 1 /* @p3 */
  public class MapeamentoChild : ClassMap<Child>
    {
        public MapeamentoChild()
        {
            CompositeId()
                .KeyReference(_ => _.Parent, "PARENT_ID")
                .KeyProperty(_ => _.ChildId, "CHILD_ID");
            HasMany(_ => _.Items)
                .AsSet()
                .Cascade.All()
                .Not.OptimisticLock()
                .KeyColumns.Add("PARENT_ID")
                .KeyColumns.Add("CHILD_ID");
            Version(Reveal.Member<Child>("version"));
        }
    }
-- statement #1
INSERT INTO [Parent]
DEFAULT VALUES;

select SCOPE_IDENTITY()

-- statement #2
INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_0 */,
             1 /* @p1_0 */,
             1 /* @p2_0 */)

INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_1 */,
             1 /* @p1_1 */,
             2 /* @p2_1 */)

INSERT INTO [Child]
            (version,
             PARENT_ID,
             CHILD_ID)
VALUES      (1 /* @p0_2 */,
             1 /* @p1_2 */,
             3 /* @p2_2 */)

-- statement #3
INSERT INTO [Item]
            (version,
             ItemId)
VALUES      (1 /* @p0_0 */,
             1 /* @p1_0 */)

-- statement #4
UPDATE [Item]
SET    PARENT_ID = 1 /* @p0_0 */,
       CHILD_ID = 1 /* @p1_0 */
WHERE  ItemId = 1 /* @p2_0 */