gpt4 book ai didi

nhibernate - 如果之前已使用 Get 检索过该实体,则无法合并具有复合 Id 的实体

转载 作者:行者123 更新时间:2023-12-03 16:45:24 25 4
gpt4 key购买 nike

我正在进行的项目要求我们系统中的数据与另一个系统中的数据同步(另一个系统非常流行,这就是同步如此重要的原因)。但是,当我尝试更新具有复合 ID 的现有实体时,我遇到了一个奇怪的问题。

问题是,只要在调用 之前检索到要更新的实体(使用 获取 )合并 ,它不起作用(更改不会持久保存到数据库,但不会引发异常)。当我删除对 的调用时获取 ,更新实体作品。需要有关实体是否存在的知识,因为如果正在创建它,则需要生成部分复合 id。

bool exists = ScanForInstance(instance);
using (var session = SessionFactoryFactory.GetSessionFactory<T>().OpenSession())
{
if (exists)
{
instance = (T)session.Merge(instance);
}
else
{
KeyGenerator.Assign<T>(instance);
newId = session.Save(instance);
}

session.Flush();
}

获取 调用 扫描实例 方法:
private bool ScanForInstance<T>(T instance)
where T : class
{
var id = IdResolver.ResolveObject<T>(instance);
using (var session = SessionFactoryFactory.GetSessionFactory<T>().OpenStatelessSession())
{
return session.Get<T>(id) != null;
}
}

IdResolver 用于确定应该对 id 使用什么(映射中单个键的值,否则对象本身用于具有复合 id 的实体)。

就像我说的,如果我取消对 的调用获取 它工作正常。它也适用于所有其他操作(创建、读取和删除)。所有操作,包括更新,都适用于具有单个键的实体。

数据库无处不在,有一定数量的限制:
  • 不,我无法更改任何架构(我认为这是对 FNB 问题的频繁响应)。
  • 我不想删除然后插入,因为有些列我们没有同步回我们的系统,我不想清除这些


  • 更新:我添加了一个简单的示例,人们可以复制/粘贴来测试这种奇怪的行为(如果它实际上是普遍的)。我希望人们会这样做至少可以确认我的问题。

    要映射的类型,Fluent 映射:
    public class ParentType
    {
    public virtual long AssignedId { get; set; }

    public virtual long? GeneratedId { get; set; }

    public virtual string SomeField { get; set; }

    public override bool Equals(object obj)
    {
    return Equals(obj as ParentType);
    }

    private bool Equals(ParentType other)
    {
    if (ReferenceEquals(this, other)) return true;
    if (ReferenceEquals(null, other)) return false;

    return AssignedId == other.AssignedId &&
    GeneratedId == other.GeneratedId;
    }

    public override int GetHashCode()
    {
    unchecked
    {
    int hash = GetType().GetHashCode();
    hash = (hash * 31) ^ AssignedId.GetHashCode();
    hash = (hash * 31) ^ GeneratedId.GetHashCode();

    return hash;
    }
    }
    }

    public class ParentMap : ClassMap<ParentType>
    {
    public ParentMap()
    {
    Table("STANDARDTASKITEM");

    CompositeId()
    .KeyProperty(x => x.AssignedId, "STANDARDTASK")
    .KeyProperty(x => x.GeneratedId, "STANDARDTASKITEM");

    Map(x => x.SomeField, "DESCRIPTION");

    Not.LazyLoad();
    }
    }

    不要介意它被称为“ParentType”。我实际上没有任何其他映射,并且在此示例中实际上并没有将该类型用作父类型。之所以这样称呼它,是因为我要打开另一个确实涉及复合 ID 和继承问题的问题( DON'T USE COMPOSITE ID'S! :-D )。

    对于实际测试,我只是在 VS 中创建了一个控制台项目,名称为 。程序.cs :
    static void Main(string[] args)
    {
    var smFactory = Fluently.Configure()
    .Database(() => new OdbcPersistenceConfigurer()
    .Driver<OdbcDriver>()
    .Dialect<GenericDialect>()
    .Provider<DriverConnectionProvider>()
    .ConnectionString(BuildSMConnectionString())
    .ProxyFactoryFactory(typeof(NHibernate.ByteCode.Castle.ProxyFactoryFactory))
    .UseReflectionOptimizer()
    .UseOuterJoin())
    .Mappings
    (m =>
    m.FluentMappings.Add<ParentMap>()
    );

    var sessionFactory = smFactory.BuildSessionFactory();

    var updatedInstance = new ParentType
    {
    AssignedId = 1,
    GeneratedId = 13,
    SomeField = "UPDATED"
    };

    bool exists;

    using (var session = sessionFactory.OpenStatelessSession())
    {
    exists = session.Get<ParentType>(updatedInstance) != null;
    }

    using (var session = sessionFactory.OpenSession())
    {
    if (exists)
    {
    session.Merge(updatedInstance);

    session.Flush();
    }
    }
    }

    private static string BuildSMConnectionString()
    {
    // Return your connection string here
    }

    class OdbcPersistenceConfigurer : PersistenceConfiguration<OdbcPersistenceConfigurer, OdbcConnectionStringBuilder>
    {

    }

    我知道添加这个示例只会稍微有帮助,因为任何想要测试它的人都需要更改 ParentType 字段以符合他们已经在他们自己的数据库中拥有的表,或者添加一个表来匹配 ParentType 中映射的内容.既然我已经在测试方面取得了良好的开端,我希望至少出于好奇有人会这样做。

    最佳答案

    好吧,我至少找到了解决问题的方法,但不知道为什么。我的解决方案是创建一个包含我用作复合 id 的属性的新类型:

    public class CompositeIdType
    {
    public virtual long AssignedId { get; set; }

    public virtual long GeneratedId { get; set; }

    public override bool Equals(object obj)
    {
    return Equals(obj as CompositeIdType);
    }

    private bool Equals(CompositeIdType other)
    {
    if (ReferenceEquals(this, other)) return true;
    if (ReferenceEquals(null, other)) return false;

    return AssignedId == other.AssignedId &&
    GeneratedId == other.GeneratedId;
    }

    public override int GetHashCode()
    {
    unchecked
    {
    int hash = GetType().GetHashCode();

    hash = (hash * 31) ^ AssignedId.GetHashCode();
    hash = (hash * 31) ^ GeneratedId.GetHashCode();

    return hash;
    }
    }
    }

    然后,替换 中的属性父类型 有关此新类型的引用:
    public class ParentType
    {
    public virtual CompositeIdType Key { get; set; }

    public virtual string SomeField { get; set; }
    }

    通过这些更改,新的映射将是:
    public class ParentMap : ClassMap<ParentType>
    {
    public ParentMap()
    {
    Table("STANDARDTASKITEM");

    CompositeId<CompositeIdType>(x => x.Key)
    .KeyProperty(x => x.AssignedId, "STANDARDTASK")
    .KeyProperty(x => x.GeneratedId, "STANDARDTASKITEM");

    Map(x => x.SomeField, "DESCRIPTION");

    Not.LazyLoad();
    }
    }

    完成所有这些更改后, 合并 即使在 获取 之前调用合并 称呼。我最好的选择是 的非泛型形式复合ID 当您调用 时,未正确执行某项操作,或者它正在制作的映射不适用于 NH。合并 在使用它的实体上(如果是这种情况,我想进入 FNH 的源来修复它,但我已经花了太多时间来弄清楚如何绕过这个问题)。

    这一切都很好,但这需要我为我正在映射的每个实体创建一个新类型,或者至少为具有不同数量键的 id 创建一个新类型(即具有 2 个键的类型,具有 3 个键的类型键等)。

    为了避免这种情况,我可以修改它,以便您添加与您正在映射的相同类型的引用,并在构造函数中设置对 this 的引用:
    public class ParentType
    {
    public ParentType()
    {
    Key = this;
    }

    public virtual ParentType Key { get; set; }

    public virtual long AssignedId { get; set; }

    public virtual long GeneratedId { get; set; }

    public virtual string SomeField { get; set; }

    public override bool Equals(object obj)
    {
    return Equals(obj as ParentType);
    }

    private bool Equals(ParentType other)
    {
    if (ReferenceEquals(this, other)) return true;
    if (ReferenceEquals(null, other)) return false;

    return AssignedId == other.AssignedId &&
    GeneratedId == other.GeneratedId;
    }

    public override int GetHashCode()
    {
    unchecked
    {
    int hash = GetType().GetHashCode();

    hash = (hash * 31) ^ AssignedId.GetHashCode();
    hash = (hash * 31) ^ GeneratedId.GetHashCode();

    return hash;
    }
    }
    }

    那么映射将是:
    public class ParentMap : ClassMap<ParentType>
    {
    public ParentMap()
    {
    Table("STANDARDTASKITEM");

    CompositeId<ParentType>(x => x.Key)
    .KeyProperty(x => x.AssignedId, "STANDARDTASK")
    .KeyProperty(x => x.GeneratedId, "STANDARDTASKITEM");

    Map(x => x.SomeField, "DESCRIPTION");

    Not.LazyLoad();
    }
    }

    我已经使用 对其进行了更新和插入测试合并 获取 在合并之前被调用,令人惊讶的是 它可以工作 .我仍然在使用哪个修复(包含复合 id 或自引用的新类型)的栅栏上,因为自引用对我的口味来说似乎有点不合时宜。

    如果有人发现为什么这最初不起作用,我仍然想知道......

    关于nhibernate - 如果之前已使用 Get 检索过该实体,则无法合并具有复合 Id 的实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6837816/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com