Каскадное удаление в Fluent NHibernate с использованием Composite ID в отношении HasMany с ограничением целостности

У меня есть ошибка, и независимо от того, насколько я Google, я не могу найти решение этой проблемы. Я прочитал и попытался решить эту проблему, используя следующие темы. Вот самые близкие, которые я нашел, связанные с моей проблемой:

Схема БД

В моем отображении участвуют четыре таблицы: Member, Vehicle, MemberVehicles а также LastUsedVehicles,

  • таблицы Member а также Vehicle бизнес-объекты в приложении.
  • Таблица MemberVehicles таблица ассоциаций (many-to-many) без лишних столбцов. Эти отношения работают без проблем.
  • Таблица LastUsedVehicles имеет составной ключ, состоящий изVehicle_uid а также Member_uid, которые являются внешними ключами к соответствующим таблицам. Стол также имеет дополнительную DateLastUsed столбец для хранения метки времени. Отображение этой таблицы - моя проблема.

Эта проблема

Когда я удаляю MemberМне нужны все его Vehicles также следует удалить (это прекрасно работает - записи удаляются из таблицы "многие ко многим"), а также все связанные LastUsedVehicles записи (это та часть, которая не работает).

Когда я выполняю этот код...

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

... я получаю эту ошибку:

System.Exception: попытка фиксации во время UnitOfWork не удалась; откат транзакции произошел из-за следующей ошибки: Указанный ключ отсутствует в словаре. ----> System.Collections.Generic.KeyNotFoundException: указанный ключ отсутствует в словаре.

Я не получаю сообщение об ошибке, когда LastUsedVehicles стол пуст

Отображения

член

public class MemberMap : ClassMap<Member>
{
    public MemberMap()
    {
        Table("member");

        HasManyToMany<Vehicle>(x => x.Vehicles) // working fine
            .Table("member_vehicles")
            .ParentKeyColumn("MEMBER_UID")
            .ChildKeyColumn("VEHICLE_UID")
            .Inverse()
            .Cascade.SaveUpdate()
            .Cascade.AllDeleteOrphan();

        HasMany(x => x.LastUsedVehicleses)
            //.Table("LASTUSED_VEHICLES")
            .KeyColumn("MEMBER_UID")
            .Fetch.Select()
            //.KeyColumns.Add("VEHICLE_UID")
            //.KeyColumns.Add("MEMBER_UID")
            .Inverse()
            .Cascade.AllDeleteOrphan();
    }
}

Транспортное средство

    public class VehicleMap : ClassMap<Vehicle>
    {
        public VehicleMap()
        {
            Table("vehicles");

            HasManyToMany(x => x.Members) // working fine
                .Table("member_vehicles")
                .ParentKeyColumn("VEHICLE_UID")
                .ChildKeyColumn("MEMBER_UID")
                .Not.LazyLoad()
                .Cascade.SaveUpdate();

            HasMany(x => x.LastUsedVehicles)
                .KeyColumn("VEHICLE_UID")
                .Inverse()
                .Fetch.Select()
                .Cascade.AllDeleteOrphan();

        }
    }

LastUsedVehicle

    public class LastUsedVehiclesMap : ClassMap<LastUsedVehicles>
    {
        public LastUsedVehiclesMap()
        {
            Table("lastused_vehicles");

            CompositeId()
                .KeyReference(x => x.Vehicle, "VEHICLE_UID")
                .KeyReference(x => x.Member, "MEMBER_UID");

            Map(x => x.DateLastUsed)
                .Column("DATELASTUSED")
                .Not.Nullable();

            // Things that I tried
            //References(x => x.Member)
            //    .Fetch.Select()
            //    .Cascade.None()
            //    .Column("MEMBER_UID");
            //
            //References(x => x.Vehicle)
            //    .Fetch.Select()
            //    .Cascade.None()
            //    .Column("VEHICLE_UID");

            //Version(x => x.DateLastUsed)
            //    .Column("DATELASTUSED")
            //    .Not.Nullable();

        }
    }

Классы

член

public class Member : PrimaryKeyBase
{
    private readonly Iesi.Collections.Generic.ISet<Vehicle> _vehicles;
    private Iesi.Collections.Generic.ISet<LastUsedVehicles> _lastUsedVehicles;

    public Member()
    {
        _vehicles = new HashedSet<Vehicle>();
        _lastUsedVehicles = new HashedSet<LastUsedVehicles>();
    }

    // ... GetHashCode ...

    public virtual Iesi.Collections.Generic.ISet<Vehicle> Vehicles
    {
        get { return _vehicles; }
    }

    public virtual void AddVehicle(Vehicle vehicle)
    {
        vehicle.AddMember(this);
        _vehicles.Add(vehicle);
    }

    public virtual Iesi.Collections.Generic.ISet<LastUsedVehicles> LastUsedVehicleses
    {
        get { return _lastUsedVehicles; }
        set { _lastUsedVehicles = value; }
    }
}

Транспортное средство

public class Vehicle : PrimaryKeyBase
{
    private Iesi.Collections.Generic.ISet<Member> _members;
    private Iesi.Collections.Generic.ISet<LastUsedVehicles> _lastUsedVehicles;

    public Vehicle()
    {
        _members = new HashedSet<Member>();
        _lastUsedVehicles = new HashedSet<LastUsedVehicles>();
    }

    public virtual Member Member
    {
        get { return _members.FirstOrDefault(); }
    }

    public virtual IEnumerable<Member> 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<LastUsedVehicles> LastUsedVehicles
    {
        get { return _lastUsedVehicles; }
    }
}

LastUsedVehicles

    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) && 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();
            }
        }

    }

Отладочная информация

Чтобы было понятнее, я также прилагаю некоторые SQL-запросы и их результаты.

// 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'));

Однажды за столом LASTUSED_VEHICLES пусто, я могу удалить участника.


Извините за длину поста, но этот сценарий кажется совершенно уникальным. Спасибо за внимание.

0 ответов

Другие вопросы по тегам