Много-ко-многим и каскады: проблема с удалением сущностей - PullRequest
0 голосов
/ 07 декабря 2010

У меня есть следующие две сущности с их отображениями:

public class VideoCategory : BaseEntity<VideoCategory>
{
    private readonly Iesi.Collections.Generic.ISet<VideoFolder> folders = new HashedSet<VideoFolder>();

    public VideoCategory()
    {

    }

    public VideoCategory(string name)
    {
        Name = name;
    }

    public string Name { get; set; }


    public IEnumerable<VideoFolder> Folders { get { return folders; } }

    public void AddFolder(VideoFolder videoFolder)
    {
        Contract.Requires(videoFolder != null);

        if (folders.Contains(videoFolder))
            return;

        folders.Add(videoFolder);
        videoFolder.AddCategory(this);
    }

    public void RemoveFolder(VideoFolder videoFolder)
    {
        folders.Remove(videoFolder);
        videoFolder.RemoveCategory(this);
    }

    public void ClearFolders()
    {
        folders.ForEach(f => f.RemoveCategory(this));

        folders.Clear();
    }
}

public class VideoFolder : BaseEntity<VideoFolder>
{
    private readonly Iesi.Collections.Generic.ISet<VideoCategory> categories = new HashedSet<VideoCategory>();

    public VideoFolder()
    {

    }

    public VideoFolder(string path)
    {
        Path = path;
    }

    public string Path { get; set; }
    public string Name { get; private set; }

    public IEnumerable<VideoCategory> Categories { get { return categories; } }

    protected internal void AddCategory(VideoCategory videoCategory)
    {
        Contract.Requires(videoCategory != null);

        categories.Add(videoCategory);
    }

    protected internal void RemoveCategory(VideoCategory videoCategory)
    {
        categories.Remove(videoCategory);
    }
}

public class VideoCategoryMap : ClassMap<VideoCategory>
{
    public VideoCategoryMap()
    {
        Table("VideoCategories");

        Id(cat => cat.Id)
            .GeneratedBy.Native();

        Map(cat => cat.Name)
            .Unique()
            .Not.Nullable();

        HasManyToMany<VideoFolder>(Reveal.Member<VideoCategory>("folders"))
            .Access.CamelCaseField()
            .AsSet()
            .Inverse()
            .Cascade.AllDeleteOrphan();
    }
}

public class VideoFolderMap : ClassMap<VideoFolder>
{
    public VideoFolderMap()
    {
        Table("VideoFolders");

        Id(folder => folder.Id)
            .GeneratedBy.Native();

        Map(folder => folder.Path)
            .Not.Nullable();

        HasManyToMany<VideoCategory>(Reveal.Member<VideoFolder>("categories"))
            .Access.CamelCaseField()
            .AsSet();
    }
}

У меня есть 2 модульных теста:

[Fact]
public void DeletingVideocategory_DeletesVideoFolders()
{
    object id;

    using (ISession session = SessionFactory.OpenSession())
    {
        var categ = new VideoCategory("Foo");
        var folder = new VideoFolder("D:\\Foo");
        categ.AddFolder(folder);

        id = session.Save(categ);

        session.Flush();
    }

    using (ISession session = SessionFactory.OpenSession())
    {
        var category = session.Get<VideoCategory>(id);
        category.ClearFolders();

        session.Delete(category);
        session.Flush();

        Assert.Equal(0, session.QueryOver<VideoFolder>().RowCount());
    }
}

[Fact]
public void DeletingVideocategory_DoesntDeleteVideoFoldersOwned_ByOtherCategories()
{
    object id;
    object id2;

    using (ISession session = SessionFactory.OpenSession())
    {
        var categ = new VideoCategory("Foo");
        var categ2 = new VideoCategory("Bar");
        var folder = new VideoFolder("D:\\Foo");

        categ.AddFolder(folder);
        categ2.AddFolder(folder);

        id = session.Save(categ);
        id2 = session.Save(categ2);

        session.Flush();
    }

    using (ISession session = SessionFactory.OpenSession())
    {
        var category = session.Get<VideoCategory>(id);
        category.ClearFolders();

        session.Delete(category);
        session.Flush();

        Assert.Equal(1, session.QueryOver<VideoFolder>().RowCount());
        Assert.Equal(1, session.Get<VideoCategory>(id2).Folders.Count());
    }
}

Первый успешный, но не первый Утверждение второго, где папка видео удалена, тогда как она все еще связана с оставшейся категорией видео.

Вот вывод SQL второго теста:

INSERT INTO VideoCategories (Name) VALUES (@p0); select last_insert_rowid();@p0 = 'Foo' [Type: String (0)]
INSERT INTO VideoFolders (Path) VALUES (@p0); select last_insert_rowid();@p0 = 'D:\Foo' [Type: String (0)]
INSERT INTO VideoCategories (Name) VALUES (@p0); select last_insert_rowid();@p0 = 'Bar' [Type: String (0)]
INSERT INTO CategoriesToFolders (VideoFolder_id, VideoCategory_id) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 1 [Type: Int32 (0)]
INSERT INTO CategoriesToFolders (VideoFolder_id, VideoCategory_id) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 2 [Type: Int32 (0)]
SELECT videocateg0_.Id as Id10_0_, videocateg0_.Name as Name10_0_ FROM VideoCategories videocateg0_ WHERE videocateg0_.Id=@p0;@p0 = 1 [Type: Int32 (0)]
SELECT folders0_.VideoCategory_id as VideoCat1_1_, folders0_.VideoFolder_id as VideoFol2_1_, videofolde1_.Id as Id13_0_, videofolde1_.Path as Path13_0_ FROM CategoriesToFolders folders0_ left outer join VideoFolders videofolde1_ on folders0_.VideoFolder_id=videofolde1_.Id WHERE folders0_.VideoCategory_id=@p0;@p0 = 1 [Type: Int32 (0)]
SELECT categories0_.VideoFolder_id as VideoFol2_1_, categories0_.VideoCategory_id as VideoCat1_1_, videocateg1_.Id as Id10_0_, videocateg1_.Name as Name10_0_ FROM CategoriesToFolders categories0_ left outer join VideoCategories videocateg1_ on categories0_.VideoCategory_id=videocateg1_.Id WHERE categories0_.VideoFolder_id=@p0;@p0 = 1 [Type: Int32 (0)]
SELECT videos0_.VideoFolder_id as VideoFol3_1_, videos0_.Id as Id1_, videos0_.Id as Id12_0_, videos0_.Path as Path12_0_ FROM VideoFiles videos0_ WHERE videos0_.VideoFolder_id=@p0;@p0 = 1 [Type: Int32 (0)]
DELETE FROM CategoriesToFolders WHERE VideoFolder_id = @p0;@p0 = 1 [Type: Int32 (0)]
DELETE FROM VideoFolders WHERE Id = @p0;@p0 = 1 [Type: Int32 (0)]
DELETE FROM VideoCategories WHERE Id = @p0;@p0 = 1 [Type: Int32 (0)]
SELECT count(*) as y0_ FROM VideoFolders this_

Если я изменяю сопоставление и изменяю Cascade.AllDeleteOrphan в VideoCategoryMap, второй тест завершается успешно, тогда как первый не проходит, поскольку потерянная папка видео не удаляется.

Как сделать оба теста успешными?

Заранее спасибо

Майк

1 Ответ

2 голосов
/ 08 декабря 2010

«Удалить сироту» в NHibernate не означает «Удалить сущности без других родителей».Это означает, что «при удалении отношения удалите также указанную сущность».

NHibernate не обрабатывает тот случай, когда вам нужен OOTB.Возможные решения включают в себя логику уровня домена, логику уровня хранилища, прослушиватели событий и триггеры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...