Отношения один к одному в NHibernate с использованием составного ключа
Я пытаюсь выяснить, как правильно смоделировать отношение «один к одному» (или «один к нулю») в NHibernate, или действительно категорически узнать, можно ли это сделать.
В настоящее время у меня есть две модели, и между ними должна быть двунаправленная связь, определяемая составным первичным ключом, состоящим из двух свойств, которые совместно используются/дублируются в обеих таблицах. A может иметь ноль или один связанный файл, и каждый ScriptDocument будет иметь связанный файл . Оба они имеют общий первичный ключ, состоящий из двух свойств: строки («ключ») и int («пользовательская ссылка»).
В настоящее время я настроил свои модели и сопоставления следующим образом:
public class Document
{
public virtual string Key { get; set; }
public virtual int UserRef { get; set; }
public virtual ScriptDocument ScriptDocument { get; set; }
// ... other properties ...
public override bool Equals(object obj)
{
return obj is Document document &&
Key == document.Key &&
UserRef == document.UserRef;
}
public override int GetHashCode()
{
return HashCode.Combine(Key, UserRef);
}
}
public class DocumentMap : ClassMapping<Document>
{
public DocumentMap()
{
Schema("Documents");
Table("Documents");
ComposedId(m =>
{
m.Property(x => x.Key);
m.Property(x => x.UserRef, m => m.Column("User_Ref"));
// the PK fields are named slightly differently across the two tables. Same data types though and same names in the models.
});
OneToOne(x => x.ScriptDocument, m => {
m.Cascade(Cascade.All);
m.Constrained(false);
});
// ... other property mappings ...
}
}
public class ScriptDocument
{
public virtual string Key { get; set; }
public virtual int UserRef { get; set; }
public virtual Document Document { get; set; }
// ... other properties ...
public override bool Equals(object obj)
{
return obj is ScriptDocument sd &&
Key == sd.Key &&
UserRef == sd.UserRef;
}
public override int GetHashCode()
{
return HashCode.Combine(Key, UserRef);
}
}
public class ScriptDocumentMap : ClassMapping<ScriptDocument>
{
public ScriptDocumentMap()
{
Table("Script_Document");
ComposedId(m =>
{
m.Property(x => x.Key, m => m.Column("DocKey"));
m.Property(x => x.UserRef);
});
OneToOne(x => x.Document, m => m.Constrained(true));
// ... other property mappings ...
}
}
На данный момент NHibernate, кажется, доволен этими моделями и определениями сопоставления, но проблема в том, что отношения, по-видимому, фактически игнорируются. При загрузке одного или нескольких объектов все они имеют нулевое свойство, и то же самое верно для
Насколько я могу судить, NHibernate ни в коем случае даже не пытается заполнить эти свойства. Поэтому я предполагаю, что происходит одно из двух:
- Я сделал что-то не так (вероятно, в маппингах). Я вроде как надеюсь, что есть одна или две мелочи, которые я пропустил, но я не могу понять, что это может быть.
- Это на самом деле не может быть сделано. Я понимаю, что этот подход должен быть просто прекрасным, если бы у нас был один общий первичный ключ, но я не уверен, что общий составной ключ — это то, что мы можем сделать. Не могу найти сопоставимых примеров.
Обратите внимание на этот подход: вам определенно не нужно говорить мне, насколько это неортодоксально, я болезненно осознаю. Но я работаю в рамках ограничений уже существующих систем. Если это абсолютно, категорически невозможно, это подход, который я хотел бы продолжить на данном этапе.
1 ответ
Таким образом, ключом к решению этой проблемы, казалось, было использование составного идентификатора компонента.
Я добавил следующий класс для определения составного первичного ключа для обеих таблиц:
[Serializable]
public class DocumentIdentifyingKey
{
public virtual string Key { get; set; }
public virtual int UserRef { get; set; }
public override bool Equals(object obj)
{
return obj is DocumentIdentifyingKey key &&
Key == key.Key &&
UserRef == key.UserRef;
}
public override int GetHashCode()
{
return HashCode.Combine(Key, UserRef);
}
public override string ToString()
{
return $"{UserRef}/{Key}";
}
}
Затем он смог обновить классы моделей сущностей и связанные сопоставления следующим образом, используя
public class Document
{
public virtual DocumentIdentifyingKey Identity { get; set; }
public virtual ScriptDocument ScriptDocument { get; set; }
// ... other properties ...
public override bool Equals(object obj)
{
return obj is Document document &&
Identity == document.Identity;
}
public override int GetHashCode()
{
return Identity.GetHashCode();
}
}
public class DocumentMap : ClassMapping<Document>
{
public DocumentMap()
{
Schema("Documents");
Table("Documents");
ComponentAsId(x => x.Identity, m => {
m.Property(i => i.Key);
m.Property(i => i.UserRef, m => m.Column("User_Ref"));
});
OneToOne(x => x.ScriptMetadata, m => {
m.Cascade(Cascade.All);
m.Constrained(false);
m.Fetch(FetchKind.Join);
m.Lazy(LazyRelation.NoLazy);
});
// ... other property mappings ...
}
}
public class ScriptMetadata
{
public virtual DocumentIdentifyingKey Identity { get; set; }
public virtual Document Document { get; set; }
// ... other properties ...
public override bool Equals(object obj)
{
return obj is ScriptMetadata sd &&
Identity == sd.Identity;
}
public override int GetHashCode()
{
return Identity.GetHashCode();
}
}
public class ScriptDocumentMap : ClassMapping<ScriptMetadata>
{
public ScriptDocumentMap()
{
Table("Script_Document");
ComponentAsId(x => x.Identity, m =>
{
m.Property(i => i.Key, m => m.Column("DocKey"));
m.Property(i => i.UserRef);
});
OneToOne(x => x.Document, m => {
m.Constrained(true);
m.Fetch(FetchKind.Join);
m.Lazy(LazyRelation.NoLazy);
});
// ... other property mappings ...
}
}
Я не совсем уверен, почему это сработало, но наличие идентификатора документа, выраженного как экземпляр объекта, а не просто комбинация двух полей в каждом классе, казалось, было ключевым заклинанием, которое позволило NHibernate понять, что я получаю в.
Примечание: в этом решении я добавил