S#arp架构自引用实体的流畅映射(树结构)

发布于 2024-08-11 11:58:06 字数 4049 浏览 2 评论 0原文

我在将 Fluent NH 映射转换为 Sharp Architecture 时遇到了问题。我喜欢这个平台,因为它很简单,但它处理实体映射的方式似乎与纯 Fluent NH 略有不同。

我有一个实体“类别”,它是一个简单的树结构。我必须覆盖自动映射,因为我需要添加一个 M:M 属性(不包含在下面的代码中)。

当我在存储库上创建测试时,GetAll 方法按其应有的方式返回所有类别,但是 Children 属性只是无限循环自身。即每个类别的子级列表仅包含其自身,处于无限循环中。

/// 实体 ///

public class Category : Entity
{
    public Category()
    {
        InitMembers();
    }

    /// <summary>
    /// Creates valid domain object
    /// </summary>
    public Category(string name)
        : this()
    {
        Name = name;
    }

    /// <summary>
    /// Creates valid domain object
    /// </summary>
    public Category(string name, int depth)
        : this()
    {
        Name = name;
        Depth = depth;
    }

    private void InitMembers()
    {
        Children = new List<Category>();
    }

    [DomainSignature]
    [NotNullNotEmpty]
    public virtual string Name { get; protected set; }

    [DomainSignature]
    public virtual int Depth { get; protected set; }

    public virtual Category Parent { get; set; }

    public virtual IList<Category> Children { get; private set; }

    public virtual void AddChild(Category category)
    {
        category.Parent = this;
        Children.Add(category);
    }
}

/// 映射 ///

public class CategoryMap : IAutoMappingOverride<Category>
{
    public void Override(AutoMap<Category> mapping)
    {
        mapping.Id(x => x.Id, "CategoryId")
            .WithUnsavedValue(0)
            .GeneratedBy.Identity();

        mapping.Map(x => x.Name).WithLengthOf(50);

        mapping.Map(x => x.Depth);

        mapping.HasMany<Category>(x => x.Children)
            .Inverse()
            .Cascade.All()
            .KeyColumnNames.Add("Parent_id")
            .AsBag();
    }
}

/// 数据存储库测试 ///

[TestFixture]
[Category("DB Tests")]
public class CategoryRepositoryTests : RepositoryTestsBase
{
    private readonly IRepository<Category> _repository = new Repository<Category>();

    protected override void LoadTestData()
    {
        CreatePersistedCategory("Root 1");
        CreatePersistedCategory("Root 2");
        CreatePersistedCategoryWithChildren("Level 1", "Level 2", "Level 3");
    }

    [Test]
    public void CanGetAllCategories()
    {
        var categories = _repository.GetAll();
        categories.ShouldNotBeNull();
        categories.Count.ShouldEqual(5);
    }

    [Test]
    public void CanGetCategoryById()
    {
        var category = _repository.Get(1);
        category.Name.ShouldEqual("Root 1");
        category.Depth.ShouldEqual(1);
    }

    [Test]
    public void CanGetCategoryChildren()
    {
        var category = _repository.Get(3);
        category.Name.ShouldEqual("Level 1");
        category.Depth.ShouldEqual(1);
        category.Children.ShouldNotBeNull();
        category.Children.Count.ShouldEqual(1);
        category.Children[0].Name.ShouldEqual("Level 2");
        category.Children[0].Depth.ShouldEqual(2);
        category.Children[0].Children.ShouldNotBeNull();
        category.Children[0].Children.Count.ShouldEqual(1);
        category.Children[0].Children[0].Name.ShouldEqual("Level 3");
        category.Children[0].Children[0].Depth.ShouldEqual(3);
    }


    private void CreatePersistedCategory(string categoryName)
    {
        var category = new Category(categoryName, 1);
        _repository.SaveOrUpdate(category);
        FlushSessionAndEvict(category);
    }

    private void CreatePersistedCategoryWithChildren(string category1, string category2, string category3)
    {
        var cat1 = new Category(category1, 1);
        var cat2 = new Category(category2, 2) { Parent = cat1 };
        var cat3 = new Category(category3, 3) { Parent = cat2 };
        cat1.AddChild(cat2);
        cat2.AddChild(cat3);
        _repository.SaveOrUpdate(cat1);
        FlushSessionAndEvict(cat1);
    }
}

I've come up against a problem in converting my Fluent NH mapping to Sharp Architecture. I like the platform for it's ease, however it seems to handle entity mappings slightly differently to pure Fluent NH.

I have a Entity 'Category' that is a simple tree structure. I have to override the auto-mapping as there is a M:M property that I need to add in (not included in code below).

When I create tests on the repository, the GetAll method returns all Categories as it should, however the Children property just infinitely loops itself. i.e. the list of children for each category only contains itself, in and unending loop.

/// The Entity ///

public class Category : Entity
{
    public Category()
    {
        InitMembers();
    }

    /// <summary>
    /// Creates valid domain object
    /// </summary>
    public Category(string name)
        : this()
    {
        Name = name;
    }

    /// <summary>
    /// Creates valid domain object
    /// </summary>
    public Category(string name, int depth)
        : this()
    {
        Name = name;
        Depth = depth;
    }

    private void InitMembers()
    {
        Children = new List<Category>();
    }

    [DomainSignature]
    [NotNullNotEmpty]
    public virtual string Name { get; protected set; }

    [DomainSignature]
    public virtual int Depth { get; protected set; }

    public virtual Category Parent { get; set; }

    public virtual IList<Category> Children { get; private set; }

    public virtual void AddChild(Category category)
    {
        category.Parent = this;
        Children.Add(category);
    }
}

/// The Mapping ///

public class CategoryMap : IAutoMappingOverride<Category>
{
    public void Override(AutoMap<Category> mapping)
    {
        mapping.Id(x => x.Id, "CategoryId")
            .WithUnsavedValue(0)
            .GeneratedBy.Identity();

        mapping.Map(x => x.Name).WithLengthOf(50);

        mapping.Map(x => x.Depth);

        mapping.HasMany<Category>(x => x.Children)
            .Inverse()
            .Cascade.All()
            .KeyColumnNames.Add("Parent_id")
            .AsBag();
    }
}

/// The Data Repository Tests ///

[TestFixture]
[Category("DB Tests")]
public class CategoryRepositoryTests : RepositoryTestsBase
{
    private readonly IRepository<Category> _repository = new Repository<Category>();

    protected override void LoadTestData()
    {
        CreatePersistedCategory("Root 1");
        CreatePersistedCategory("Root 2");
        CreatePersistedCategoryWithChildren("Level 1", "Level 2", "Level 3");
    }

    [Test]
    public void CanGetAllCategories()
    {
        var categories = _repository.GetAll();
        categories.ShouldNotBeNull();
        categories.Count.ShouldEqual(5);
    }

    [Test]
    public void CanGetCategoryById()
    {
        var category = _repository.Get(1);
        category.Name.ShouldEqual("Root 1");
        category.Depth.ShouldEqual(1);
    }

    [Test]
    public void CanGetCategoryChildren()
    {
        var category = _repository.Get(3);
        category.Name.ShouldEqual("Level 1");
        category.Depth.ShouldEqual(1);
        category.Children.ShouldNotBeNull();
        category.Children.Count.ShouldEqual(1);
        category.Children[0].Name.ShouldEqual("Level 2");
        category.Children[0].Depth.ShouldEqual(2);
        category.Children[0].Children.ShouldNotBeNull();
        category.Children[0].Children.Count.ShouldEqual(1);
        category.Children[0].Children[0].Name.ShouldEqual("Level 3");
        category.Children[0].Children[0].Depth.ShouldEqual(3);
    }


    private void CreatePersistedCategory(string categoryName)
    {
        var category = new Category(categoryName, 1);
        _repository.SaveOrUpdate(category);
        FlushSessionAndEvict(category);
    }

    private void CreatePersistedCategoryWithChildren(string category1, string category2, string category3)
    {
        var cat1 = new Category(category1, 1);
        var cat2 = new Category(category2, 2) { Parent = cat1 };
        var cat3 = new Category(category3, 3) { Parent = cat2 };
        cat1.AddChild(cat2);
        cat2.AddChild(cat3);
        _repository.SaveOrUpdate(cat1);
        FlushSessionAndEvict(cat1);
    }
}

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

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

发布评论

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

评论(3

累赘 2024-08-18 11:58:06

经过多次映射调整后,设法解决了这个问题。自动映射的东西虽然很酷,但需要一些理解。 RTFM 对我来说...

Managed to work it out, after much Mapping tweaking. The Auto-mapping stuff although very cool requires some understanding. RTFM for me...

嘴硬脾气大 2024-08-18 11:58:06

是的,我还没有发现或理解自动映射约定:TableNameConvention、PrimaryKeyConvention,特别是 HasManyConvention。默认的S#arp代码喜欢将其数据库表复数化,并且具有带有表名前缀的Id列,即CategoryId。

我不喜欢复数,我喜欢一致的 Id 列,“Id”就足够了。我的外键引用风格不同,我喜欢 Category_id。

public class HasManyConvention : IHasManyConvention
{
    public bool Accept(IOneToManyCollectionInstance oneToManyPart)
    {
        return true;
    }

    public void Apply(IOneToManyCollectionInstance oneToManyPart)
    {
        oneToManyPart.KeyColumnNames.Add(oneToManyPart.EntityType.Name + "_id");
    }
}

public class PrimaryKeyConvention : IIdConvention
{
    public bool Accept(IIdentityInstance id)
    {
        return true;
    }

    public void Apply(IIdentityInstance id)
    {
        id.Column("Id");
    }
}

然而,现在这一切都很有效,但我现在遇到了多对多映射的问题。看来 S#arp 还不太支持它们。我的映射覆盖似乎不起作用,没有任何内容插入到数据库中的映射表中。

请参阅:S#arp 架构多对多映射覆盖不工作

Right you are, I hadn't yet discovered or understood the Auto-mapping conventions: TableNameConvention, PrimaryKeyConvention, and specifically HasManyConvention. The default S#arp code likes to pluralise its database tables, and have Id columns with the table name prefixed, i.e. CategoryId.

I don't like to pluralise, and I like consistent Id columns, 'Id' suffices. And my foreign key references were different style to, I like Category_id.

public class HasManyConvention : IHasManyConvention
{
    public bool Accept(IOneToManyCollectionInstance oneToManyPart)
    {
        return true;
    }

    public void Apply(IOneToManyCollectionInstance oneToManyPart)
    {
        oneToManyPart.KeyColumnNames.Add(oneToManyPart.EntityType.Name + "_id");
    }
}

public class PrimaryKeyConvention : IIdConvention
{
    public bool Accept(IIdentityInstance id)
    {
        return true;
    }

    public void Apply(IIdentityInstance id)
    {
        id.Column("Id");
    }
}

However now this all works a treat but I now have a problem with Many-to-many mappings. It seems S#arp doesn't quite support them yet. My mapping overrides don't seem to work, nothing gets inserted into my mapping table in the database.

See: S#arp Architecture many-to-many mapping overrides not working

眼泪都笑了 2024-08-18 11:58:06

我无法使用流畅的约定来解决这个问题,并且从我所看到的情况来看,目前无法使用约定来完成此问题的搜索。 Fluent 假设像这样的自引用树是多对多的,所以在你的情况下,我假设你正在尝试映射多对多关系,所以应该没有问题。

就我而言,我需要将其映射为多对一(对于嵌套评论和回复的严格层次结构)。我能想到的唯一方法是为父子类设置一个覆盖来映射该关系。

我很想知道是否有一种方法可以通过约定成功地映射多对一。

I was not able to solve this using fluent conventions and from what I have seen searching around this currently can't be done using conventions. Fluent assumes that a self-referencing tree like this is many-to-many, so in your case I assume you are trying to map a many-to-many relationship and so there should be no problem.

In my case I needed to map it as many-to-one (for a strict hierarchy of nested comments and replies). The only way I could figure out to do this was setting an override for the parent-child class to map that relationship. Fortunately it is very easy.

I would love to know if there is a way to successfully map many-to-one like this with conventions though.

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