我正在进行的项目要求我们系统中的数据与另一个系统的数据同步(另一个系统非常流行,这就是同步如此重要的原因)。但是,当我尝试更新具有复合 ID 的现有实体时,我遇到了一个奇怪的问题。
问题是,每当检索要更新的实体时(使用Get) 调用之前Merge,它不起作用(更改不会持久化到数据库,但不会引发异常)。当我删除对Get,更新实体作品。需要了解实体是否存在,因为如果正在创建实体,则需要生成部分复合 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();
}
The Get调用是在扫描实例 method:
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;
}
}
The 身份解析器用于确定 id 的用途(映射中单个键的值,否则为具有复合 id 的实体的对象本身)。
就像我说的,如果我删除对Get效果很好。它也适用于所有其他操作(创建、读取和删除)。所有操作(包括更新)对于具有单键的实体都可以正常工作。
DB 是普遍存在的,并且存在一定数量的限制:
- 不,我无法更改任何模式(我认为这是对 FNB 问题的频繁响应)。
- 我不想先删除然后插入,因为有些列我们没有同步回我们的系统,我不想删除它们
UPDATED:我添加了一个简单的示例,人们可以复制/粘贴来测试这种奇怪的行为(如果它实际上是通用的)。我希望人们这样做至少可以确认我的问题。
要映射的类型,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 和继承的问题(不要使用复合 ID! :-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 中映射的内容。既然我已经在测试方面取得了良好的开端,我希望有人至少会出于好奇而这样做。