Note that there are some explanatory texts on larger screens.

plurals
  1. POCascade delete in Fluent NHibernate using Composite ID on a HasMany Relationship with Integrity constraint
    primarykey
    data
    text
    <p>I have an error, and no matter how much I Google it I cannot find a fix for it. I have read and tried to solve it using the following topics. These are the closest I've found related to my issue:</p> <ul> <li><a href="http://marekblotny.blogspot.com/2009/02/fluent-nhbernate-and-collections.html" rel="nofollow">Fluent NHibernate and Collections Mapping</a></li> <li><a href="https://groups.google.com/d/topic/nhusers/7M-MI8RLalg/discussion" rel="nofollow">How to cascade Save with CompositeId in NHibernate?</a></li> <li><a href="https://groups.google.com/d/topic/nhusers/OwyWQrsqTqc/discussion" rel="nofollow">How to map an objectified Many to Many relationship?</a></li> </ul> <h2>DB Schema</h2> <p>Four tables are involved in my mapping: <code>Member</code>, <code>Vehicle</code>, <code>MemberVehicles</code> and <code>LastUsedVehicles</code>.</p> <ul> <li>Tables <code>Member</code> and <code>Vehicle</code> are business objects in the application.</li> <li>Table <code>MemberVehicles</code> is an association table (<code>many-to-many</code>) with no extra columns. This relationship works with no issues.</li> <li>Table <code>LastUsedVehicles</code> has a composite key consisting of <code>Vehicle_uid</code> and <code>Member_uid</code>, which are foreign keys to those corresponding tables. The table also has an extra <code>DateLastUsed</code> column to store the timestamp. <strong>The mapping of this table is my issue.</strong></li> </ul> <h2>The Problem</h2> <p>When I delete a <code>Member</code>, I need all of his <code>Vehicle</code>s to also be deleted (this works fine - the records are deleted from the many-to-many table), as well as all of the associated <code>LastUsedVehicles</code> records (this is the part that does not work).</p> <p>When I execute this code...</p> <pre><code> try { using (IUnitOfWork unitOfWork = unitOfWorkFactory.Create()) { Member member = nHibernateMemberRepository.GetMemberBySomeMethod(x,y) if (member != null) { if (nHibernateMemberRepository.Delete(unitOfWork, member)) { unitOfWork.Commit(); } } } } catch (Exception) { throw; } </code></pre> <p>... I receive this error:</p> <blockquote> <p>System.Exception : Attempt to commit during UnitOfWork failed; transaction was rolled back due to the following error: The given key was not present in the dictionary. ----> System.Collections.Generic.KeyNotFoundException : The given key was not present in the dictionary.</p> </blockquote> <p>I do not receive an error when the <code>LastUsedVehicles</code> table is empty.</p> <h2>Mappings</h2> <h3>Member</h3> <pre><code>public class MemberMap : ClassMap&lt;Member&gt; { public MemberMap() { Table("member"); HasManyToMany&lt;Vehicle&gt;(x =&gt; x.Vehicles) // working fine .Table("member_vehicles") .ParentKeyColumn("MEMBER_UID") .ChildKeyColumn("VEHICLE_UID") .Inverse() .Cascade.SaveUpdate() .Cascade.AllDeleteOrphan(); HasMany(x =&gt; x.LastUsedVehicleses) //.Table("LASTUSED_VEHICLES") .KeyColumn("MEMBER_UID") .Fetch.Select() //.KeyColumns.Add("VEHICLE_UID") //.KeyColumns.Add("MEMBER_UID") .Inverse() .Cascade.AllDeleteOrphan(); } } </code></pre> <h3>Vehicle</h3> <pre><code> public class VehicleMap : ClassMap&lt;Vehicle&gt; { public VehicleMap() { Table("vehicles"); HasManyToMany(x =&gt; x.Members) // working fine .Table("member_vehicles") .ParentKeyColumn("VEHICLE_UID") .ChildKeyColumn("MEMBER_UID") .Not.LazyLoad() .Cascade.SaveUpdate(); HasMany(x =&gt; x.LastUsedVehicles) .KeyColumn("VEHICLE_UID") .Inverse() .Fetch.Select() .Cascade.AllDeleteOrphan(); } } </code></pre> <h3>LastUsedVehicle</h3> <pre><code> public class LastUsedVehiclesMap : ClassMap&lt;LastUsedVehicles&gt; { public LastUsedVehiclesMap() { Table("lastused_vehicles"); CompositeId() .KeyReference(x =&gt; x.Vehicle, "VEHICLE_UID") .KeyReference(x =&gt; x.Member, "MEMBER_UID"); Map(x =&gt; x.DateLastUsed) .Column("DATELASTUSED") .Not.Nullable(); // Things that I tried //References(x =&gt; x.Member) // .Fetch.Select() // .Cascade.None() // .Column("MEMBER_UID"); // //References(x =&gt; x.Vehicle) // .Fetch.Select() // .Cascade.None() // .Column("VEHICLE_UID"); //Version(x =&gt; x.DateLastUsed) // .Column("DATELASTUSED") // .Not.Nullable(); } } </code></pre> <h2>Classes</h2> <h3>Member</h3> <pre><code>public class Member : PrimaryKeyBase { private readonly Iesi.Collections.Generic.ISet&lt;Vehicle&gt; _vehicles; private Iesi.Collections.Generic.ISet&lt;LastUsedVehicles&gt; _lastUsedVehicles; public Member() { _vehicles = new HashedSet&lt;Vehicle&gt;(); _lastUsedVehicles = new HashedSet&lt;LastUsedVehicles&gt;(); } // ... GetHashCode ... public virtual Iesi.Collections.Generic.ISet&lt;Vehicle&gt; Vehicles { get { return _vehicles; } } public virtual void AddVehicle(Vehicle vehicle) { vehicle.AddMember(this); _vehicles.Add(vehicle); } public virtual Iesi.Collections.Generic.ISet&lt;LastUsedVehicles&gt; LastUsedVehicleses { get { return _lastUsedVehicles; } set { _lastUsedVehicles = value; } } } </code></pre> <h3>Vehicle</h3> <pre><code>public class Vehicle : PrimaryKeyBase { private Iesi.Collections.Generic.ISet&lt;Member&gt; _members; private Iesi.Collections.Generic.ISet&lt;LastUsedVehicles&gt; _lastUsedVehicles; public Vehicle() { _members = new HashedSet&lt;Member&gt;(); _lastUsedVehicles = new HashedSet&lt;LastUsedVehicles&gt;(); } public virtual Member Member { get { return _members.FirstOrDefault(); } } public virtual IEnumerable&lt;Member&gt; Members { get { return _members; } } public virtual void AddMember(Member member) { _members.Add(member); } public virtual void AddLastUsedVehicle(LastUsedVehicles lastUsedVehicles) { lastUsedVehicles.Vehicle = this; lastUsedVehicles.Member = this.Member; _lastUsedVehicles.Add(lastUsedVehicles); } public virtual void ClearLastUsedVehicles() { _lastUsedVehicles.Clear(); } public virtual LastUsedVehicles LastUsedVehicle { get { return _lastUsedVehicles.FirstOrDefault(); } } public virtual IEnumerable&lt;LastUsedVehicles&gt; LastUsedVehicles { get { return _lastUsedVehicles; } } } </code></pre> <h3>LastUsedVehicles</h3> <pre><code> public class LastUsedVehicles : CompositeKeyBase { public virtual Vehicle Vehicle { get; set; } public virtual Member Member { get; set; } public virtual DateTime DateLastUsed { get; set; } public virtual bool Equals(LastUsedVehicles other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Equals(other.Vehicle, Vehicle) &amp;&amp; Equals(other.Member, Member); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != typeof(LastUsedVehicles)) return false; return Equals((LastUsedVehicles)obj); } public override int GetHashCode() { unchecked { return ((Vehicle != null ? Vehicle.GetHashCode() : 0) * 397) ^ Member.GetHashCode() ^ DateLastUsed.GetHashCode(); } } } </code></pre> <h2>Debugging Info</h2> <p>To make it clearer, I'm also attaching some SQL queries and their results.</p> <pre><code>// Delete a member delete from member m where account = 'X'; // FAIL - Child record found - The member has vehicles - So makes sense // Delete all vehicles from a member from vehicles table delete from vehicles mv where mv.vehicle_uid = (select mv.vehicle_uid from member_vehicles mv where member_uid = ( select mm.member_uid from member mm where account = 'X')); // // Delete all vehicles from a member from association table delete from member_vehicles mv where member_uid = ( select mm.member_uid from member mm where account = 'X'); // Delete data on LASTUSED_VEHICLES - works delete from LASTUSED_VEHICLES lv where lv.vehicle_uid = (select mv.vehicle_uid from membervehicles mv where member_uid = ( select mm.member_uid from member mm where account = 'X')); </code></pre> <p>Once the table <code>LASTUSED_VEHICLES</code> is empty, I can delete a member.</p> <hr> <p>Sorry for the length of the post but this scenario seems to be quite unique. Thanks for the attention.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload