Как сопоставить сущность как свойство класса другой сущности, используя сопоставление по коду (NHibernate)

Я хочу, чтобы текстовые свойства моего объекта сохранялись на нескольких языках. Для этого я просто хочу сохранить FK в таблице TextContent (которая в основном содержит только ключ) и в отдельной таблице перевода, которая содержит строку для каждого перевода.

Вот моя модель базы данных

И мои соответствующие сущности

      public sealed class Translation : IEquatable<Translation>
{
    public static bool operator ==(Translation left, Translation right) => Equals(left, right);
    public static bool operator !=(Translation left, Translation right) => !Equals(left, right);

    // ORM-only constructor
    private Translation() { }

    public Translation(int languageId, string text)
    {
        _language = languageId;
        _text = text;
    }

    public bool Equals(Translation other) ...
    public override bool Equals(object obj) ...
    public override int GetHashCode() ...

    public int LanguageId => _language;
    public string Text => _text;

    private readonly int _languageId;
    private readonly string _text;
}

public class TextContent
{
    // ORM-only constructor
    protected TextContent() { }

    public TextContent(int Id, List<Translation> translations)
    {
        _id = Id;
        _originalLanguage = translations.First().LanguageId;
        _originalText = translations.First().Text;
        _translations = translations;
    }

    public virtual int Id => _id;
    public virtual int OriginalLanguageId => _originalLanguage;
    public virtual string OriginalText => _originalText;
    public virtual IList<Translation> Translations => _translations;

    private readonly int _id;
    private readonly int _originalLanguageId;
    private readonly string _originalText;
    private readonly IList<Translation> _translations;
}

public partial class Product
{
    // ORM-only constructor
    protected Product() { }

    public Product(int Id, TextContent name, TextContent description)
    {
        _id = Id;
        _nameId = name.Id;
        _name = name;
        _descriptionId = description.Id;
        _description = description;
    }

    public virtual int Id => _id;
    public virtual TextContent Name => _name;
    public virtual TextContent Description => _description;

    private int _id;
    private int _nameId;
    private TextContent _name;
    private int _descriptionId;
    private TextContent _description;
}

Сопоставления, которые я пробовал

      public class TextContentMapping : ClassMapping<TextContent>
{
    public TextContentMapping()
    {
        Table("TextContent");

        Id(content => content.Id);
        Property(content => content.OriginalLanguage);
        Property(content => content.OriginalText);

        Bag(
            content => content.Translations,
            mapper =>
            {
                mapper.Table(nameof(Translation));
                mapper.Cascade(Cascade.All);
                mapper.Key(
                    keyMapper =>
                    {
                        keyMapper.Column(columnMapper => columnMapper.Name("TextContentId"));
                        keyMapper.NotNullable(true);
                    });
            },
            relation => relation.Component(
                mapper =>
                {
                    mapper.Property(translation => translation.LanguageId);
                    mapper.Property(translation => translation.Text);
                }));
    }
}

public class ProductMapping : ClassMapping<Product>
{
    public ProductMapping()
    {
        Table(nameof(Product));

        Id(product=> product.Id, mapper => mapper.Access(Accessor.Field));
        Property(
            "_nameId",
            mapper =>
            {
                mapper.Column("NameId");
                mapper.Access(Accessor.Field);
                mapper.NotNullable(true);
            });
        Property(
            "_descriptionId",
            mapper =>
            {
                mapper.Column("DescriptionId");
                mapper.Access(Accessor.Field);
                mapper.NotNullable(true);
            });

        OneToOne(
            product => product.Name,
            mapper =>
            {
                mapper.Constrained(true);
                mapper.Cascade(Cascade.All);
                mapper.Access(Accessor.Field);
                mapper.Class(typeof(TextContent));
            });

        OneToOne(
            product => product.Description,
            mapper =>
            {
                mapper.Constrained(true);
                mapper.Cascade(Cascade.All);
                mapper.Access(Accessor.Field);
                mapper.Class(typeof(TextContent));
            });
    }
}

Как вы можете догадаться, вставка работает, а выбор - нет, поскольку мое текущее сопоставление не обеспечивает связь между_nameIdиTextContent Nameсущность ? Есть ли способ сделать это? Если нет, то какие сопоставления и объекты предметной области будут лучше, соответствующие моей модели базы данных?

1 ответ

Сопоставление OneToOne — это когда у другой стороны есть внешний ключ. Это должно быть ManyToOne. Использование этих классов

      public class TextContent
{
    // ORM-only constructor
    protected TextContent() { }
    public TextContent(int languageId, string text) : this()
    {
        OriginalLanguageId = languageId;
        OriginalText = text;
        Translations = new Dictionary<int, string>();
    }

    public virtual int Id { get; protected set; }
    public virtual int OriginalLanguageId { get; protected set; }
    public virtual string OriginalText { get; protected set; }
    public virtual IDictionary<int, string> Translations { get; protected set; }
}

public class Product
{
    // ORM-only constructor
    protected Product() { }

    public Product(TextContent name, TextContent description)
    {
        Name = name;
        Description = description;
    }

    public virtual int Id { get; protected set; }
    public virtual TextContent Name { get; protected set; }
    public virtual TextContent Description { get; protected set; }
}

и эти отображения

      public class TextContentMapping : ClassMapping<TextContent>
{
    public TextContentMapping()
    {
        Table("TextContent");

        Id(content => content.Id, m => m.Generator(Generators.Native));
        Property(content => content.OriginalLanguageId);
        Property(content => content.OriginalText);

        Map(
            content => content.Translations,
            mapper =>
            {
                mapper.Table("Translation");
                mapper.Cascade(Cascade.All);
                mapper.Key(
                    keyMapper =>
                    {
                        keyMapper.Column(columnMapper => columnMapper.Name("TextContentId"));
                        keyMapper.NotNullable(true);
                    });
            },
            keymapping => keymapping.Element(m => m.Column("LanguageId")),
            elementMapping => elementMapping.Element(m => m.Column("Text"))
            );
    }
}

public class ProductMapping : ClassMapping<Product>
{
    public ProductMapping()
    {
        Table(nameof(Product));

        Id(product => product.Id, m => m.Generator(Generators.Native));

        ManyToOne(
            product => product.Name,
            mapper =>
            {
                mapper.Column("NameId");
                mapper.Cascade(Cascade.All);
                mapper.NotNullable(true);
            });

        ManyToOne(
            product => product.Description,
            mapper =>
            {
                mapper.Column("DescriptionId");
                mapper.Cascade(Cascade.All);
                mapper.NotNullable(true);
            });
    }
}

следующий код работает

              session.Save(new Product(new TextContent(1, "someText") { Translations = { { 2, "translated Text" } } }, new TextContent(1, "some description")));
        session.Flush();
        session.Clear();

        var p = session.Query<Product>().ToList();
        var s = p[0].Name.Translations[2];
Другие вопросы по тегам