0

I have the following class tructure.

public class SearchTarget : IEntity {
    public virtual String Name { get; set; }
}

public partial class PoliceAssistance {
    public virtual Search SearchWithWarrant { get; set; }
    public virtual Search SearchWithoutWarrant { get; set; }

    public class Search : IEntityComponent {
        public virtual IList<SearchTarget> Targets { get; set; }
    }
}

IEntityComponent ensures that PoliceAssistance.Search is treated as a component in Fluent NHibernate automapping, that is, SearchWithWarrant and SearchWithoutWarrant are stored in the same PoliceAssistance table.

The problem

PoliceAssistance.Search.Targets and SearchTarget must have a many-to-many relationship — one search can contain many targets and one target can show up in many searches.

If I specify unidirectional .HasManyToMany() mapping on PoliceAssistance.Search, I get a "null value violates non-null constraint" when I try to save the entities — even if both SearchWithWarrant and SearchWithoutWarrant are instantiated and have at least one Target in the list.

If I try to specify the mapping bidirectionally, by introducing public virtual IList<PoliceAssistance.Search> InSearches { get; set; } property into SearchTarget and mapping it with .HasManyToMany().Inverse(), I get a mapping error saying that PoliceAssistance.Search cannot be referenced because it is not mapped (I guess types mapped as components aren't considered mapped?).

How should I solve this problem?

Modus Operandi
  • 552
  • 1
  • 6
  • 24

2 Answers2

0

I don't know anything about the Fluent NHibernate engine but a many-to-many connection is always made with a link-table/object. So I would expect that you would have something like this:

public class SearchTarget : IEntity
{
    public virtual String Name { get; set; }
    public virtual IList<Search_SearchTarget_Link> Searches { get; set; }
}

public class Search : IEntityComponent
{
    public virtual IList<Search_SearchTarget_Link> Targets { get; set; }
}

public class Search_SearchTarget_Link : IEntity
{
    public virtual Search search { get; set; }
    public virtual SearchTarget searchtarget { get; set; }
}

public partial class PoliceAssistance
{
    public virtual Search SearchWithWarrant { get; set; }
    public virtual Search SearchWithoutWarrant { get; set; }
}

Hope this helps...

ikwillem
  • 1,044
  • 1
  • 12
  • 24
  • Nhibernate does this for you. You define a relation and any tables (if required) are created and linked automatically. – Modus Operandi May 29 '13 at 09:34
  • Yes I already understood that it would do something like that, but all I'm saying is that a many-to-many relationship is done by linking 2 entities toghether with 1 link entity. I think the thing you are doing now cannot be translated to an Database model by the framework because it's not a valid database many-to-many relationship, this trick only works in code. – ikwillem May 29 '13 at 10:14
  • Unless I misunderstand your point, that is, again, exactly what NHibernate does — it creates the link table and manages the translation to the code side. I have done it before, and it has worked in many different scenarios (even one almost exactly like this, except with one property instead of two). But your answer is right in the sense that I will have to do that if I can't find a "proper" solution. – Modus Operandi May 29 '13 at 11:01
  • Okey, but if you take a look at your code, how is the SearchTarget connected to the Search? It's not, therefore it's not a valid many-to-many connection. – ikwillem May 29 '13 at 11:07
  • `PoliceAssistance.Search.Target` is a list of `SearchTarget`s, that's how NHibernate knows they're connected. When correctly mapped, NHibernate creates the intermediary table that contains both `SearchTarget` and `PoliceAssistance.Search.Target` IDs and uses that to automatically load/save that list along with the rest of the object. – Modus Operandi May 29 '13 at 11:57
  • Yes I understand, but that is a one-to-many connection, not many-to-many. Anyways, if that is what you are searching for I would try to move the class definition next to the other class definitions (like my example) or move the SearchTarget definition to inside the Search class. Otherwise I'm out of suggestions. – ikwillem May 29 '13 at 12:10
0

After fighting with this some more, I gave up on trying to get the mapping working with PoliceAssistance.Search as component, and instead turned it into a separate entity. This required least amount of changes and works as expected:

// Classes
public class SearchTarget : IEntity {
    public virtual String Name { get; set; }
}

public partial class PoliceAssistance {
    public virtual Search SearchWithWarrant { get; set; }
    public virtual Search SearchWithoutWarrant { get; set; }

    public class Search : IEntity {
        public virtual int Id { get; set; }
        public virtual IList<SearchTarget> Targets { get; set; }
    }
}

// Mapping
public class PoliceAssistanceMap : IAutoMappingOverride<PoliceAssistance> {
    public void Override(AutoMapping<PoliceAssistance> map) {
        map.References(x => x.SearchWithWarrant)
           .Cascade.All();
        map.References(x => x.SearchWithoutWarrant)
           .Cascade.All();
    }
}

public class SearchMap : IAutoMappingOverride<PoliceAssistance.Search> {
    public void Override(AutoMapping<PoliceAssistance.Search> mapping) {
        mapping.HasManyToMany(x => x.Targets);
    }
}
Modus Operandi
  • 552
  • 1
  • 6
  • 24