多对多和级联:实体删除问题

发布于 2024-10-06 11:12:40 字数 6420 浏览 5 评论 0原文

我有以下两个实体及其映射:

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_

如果我更改映射,并修改 VideoCategoryMap 中的 Cascade.AllDeleteOrphan,则第二个测试会成功,而第一个测试会失败,因为孤立的视频文件夹未删除。

我怎样才能使这两项测试都成功?

预先感谢

迈克

I have the following two entities, with their mappings:

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

I have these 2 unit-tests:

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

The first one succeeds, but not the first Assert of the second, where the video folder is deleted, whereas it is still associated with the remaining video category.

Here is the SQL output of the second test:

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_

If I change the mapping, and modify Cascade.AllDeleteOrphan in VideoCategoryMap, the second test succeeds, whereas the first fails, because the orphaned video folder isn't deleted.

How can I make both tests to succeed?

Thanks in advance

Mike

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

晨敛清荷 2024-10-13 11:12:40

NHibernate 中的“删除孤儿”并不意味着“删除没有任何其他父项的实体”。它的意思是“删除关系时,也删除引用的实体”。

NHibernate 不处理您需要 OOTB 的情况。可能的解决方案包括域级逻辑、存储库级逻辑、事件侦听器和触发器。

"Delete orphan" in NHibernate doesn't mean "Delete entities without any other parents". It means "when deleting the relationship, delete the referenced entity too".

NHibernate doesn't handle the case that you need OOTB. Possible solutions include domain-level logic, repository-level logic, event listeners and triggers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文