Search code examples
c#.netfluent-nhibernatemany-to-manyautomapping

Mapping a many-to-many relation with Fluent NHibernate where one of the relations is a component


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?


Solution

  • 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);
        }
    }