Мне нужно сопоставление Fluent NHibernate, которое выполнит следующее (если ничего другого, я также возьму соответствующее сопоставление NHibernate XML и проведу обратный инжиниринг).
информация
У меня есть отношение многие ко многим между двумя сущностями: Parent
и Child
. Это достигается с помощью дополнительной таблицы для хранения идентификационных данных Родителя и Ребенка. Однако мне также нужно определить два дополнительных столбца в этом отображении, которые предоставляют больше информации об отношениях.
Примерно так я определил свои типы, по крайней мере соответствующие части (где Entity
- это некоторый базовый тип, который предоставляет свойство Id
и проверяет эквивалентность на основе этого идентификатора):
public class Parent : Entity
{
public virtual IList<ParentChildRelationship> Children { get; protected set; }
public virtual void AddChildRelationship(Child child, int customerId)
{
var relationship = new ParentChildRelationship
{
CustomerId = customerId,
Parent = this,
Child = child
};
if (Children == null) Children = new List<ParentChildRelationship>();
if (Children.Contains(relationship)) return;
relationship.Sequence = Children.Count;
Children.Add(relationship);
}
}
public class Child : Entity
{
// child doesn't care about its relationships
}
public class ParentChildRelationship
{
public int CustomerId { get; set; }
public Parent Parent { get; set; }
public Child Child { get; set; }
public int Sequence { get; set; }
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
var other = obj as ParentChildRelationship;
if (return other == null) return false;
return (CustomerId == other.CustomerId
&& Parent == other.Parent
&& Child == other.Child);
}
public override int GetHashCode()
{
unchecked
{
int result = CustomerId;
result = Parent == null ? 0 : (result*397) ^ Parent.GetHashCode();
result = Child == null ? 0 : (result*397) ^ Child.GetHashCode();
return result;
}
}
}
Таблицы в базе данных выглядят примерно так (предполагается, что первичные / внешние ключи и простите синтаксис):
create table Parent (
id int identity(1,1) not null
)
create table Child (
id int identity(1,1) not null
)
create table ParentChildRelationship (
customerId int not null,
parent_id int not null,
child_id int not null,
sequence int not null
)
У меня все в порядке с Parent.Children, который является ленивым загруженным свойством. Тем не менее, ParentChildRelationship должен стремиться загрузить ParentChildRelationship.Child. Кроме того, я хочу использовать Join, когда я жду загрузки.
SQL при обращении к Parent.Children, NHibernate должен сгенерировать эквивалентный запрос к:
SELECT * FROM ParentChildRelationship rel LEFT OUTER JOIN Child ch ON rel.child_id = ch.id WHERE parent_id = ?
ОК, для этого у меня есть отображения, которые выглядят так:
ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Table("Parent");
Id(c => c.Id).GeneratedBy.Identity();
HasMany(c => c.Children).KeyColumn("parent_id");
}
}
ChildMap : ClassMap<Child>
{
public ChildMap()
{
Table("Child");
Id(c => c.Id).GeneratedBy.Identity();
}
}
ParentChildRelationshipMap : ClassMap<ParentChildRelationship>
{
public ParentChildRelationshipMap()
{
Table("ParentChildRelationship");
CompositeId()
.KeyProperty(c => c.CustomerId, "customerId")
.KeyReference(c => c.Parent, "parent_id")
.KeyReference(c => c.Child, "child_id");
Map(c => c.Sequence).Not.Nullable();
}
}
Итак, в моем тесте, если я попытаюсь получить myParentRepo.Get(1).Children
, он действительно получит мне все отношения и, когда я получаю доступ к ним из отношений, к дочерним объектам (например, я могу захватить их все, выполнив parent.Children.Select(r => r.Child).ToList()
).
Однако SQL, который генерирует NHibernate, неэффективен. Когда я обращаюсь к parent.Children, NHIbernate делает SELECT * FROM ParentChildRelationship WHERE parent_id = 1
, а затем SELECT * FROM Child WHERE id = ?
для каждого ребенка в каждом отношении. Я понимаю, почему NHibernate делает это, но я не могу понять, как настроить сопоставление, чтобы сделать запрос NHibernate так, как я упоминал выше.