Skip to content
MSugiura edited this page Dec 30, 2023 · 5 revisions

Mapping

class, table

Use DbTableAttribute for mapping classes and tables.

If the table name is omitted, the table name will be the class name converted to snake case.

[DbTable("blogs")]
public class Blog
{
}

property, column

Use DbColumnAttribute to map properties and columns.

Column type name is required.

If the column name is omitted, the column name will be the property name converted to snake case.

Specify primary key, autonumber, and unique key properties as required.

[DbTable("blogs")]
public class Blog
{
    [DbColumn("serial8", IsAutoNumber = true, IsPrimaryKey = true)]
    public int BlogId { get; set; }
}

parent relation

DbParentRelationColumnAttribute can represent a join condition with objects that have a relationship of 0 or 1.

DbParentRelationColumnAttribute specifies the type name, its own column name, and the destination "property" name.

DbParentRelationColumnAttribute is AllowMultiple.

If the column name is omitted, the property name + "id" will be treated as the column name.

Normally it is a 1:1 relationship, but in the case of a nullable type, it is a 1:0 or 1 relationship.

[DbTable("posts")]
public class Post
{
	[DbColumn("serial8", IsAutoNumber = true, IsPrimaryKey = true)]
	public int? PostId { get; set; }

	[DbParentRelationColumn("bigint", nameof(Post.Blog.BlogId))]
	public Blog Blog { get; set; } = null!;
}

NOTE: BlogId property is not required

Notice that table 'posts' has columns 'post_id', 'blog_id', but no BlogId property. The column "blog_id" is mapped to the Blog property.

[DbTable("posts")]
public class Post
{
    [DbColumn("serial8", IsAutoNumber = true, IsPrimaryKey = true)]
    public int? PostId { get; set; }

    // not good
    [DbColumn("bigint")]
    public int BlogId { get; set; }
}

child relation

To express a relationship of 0 or N, specify DbChildrenAttribute.

The type of the property to which attributes should be attached should be a list type such as IList type, which allows only specific types to be registered.

Different classes must have properties that refer to their parents.

[DbTable("blogs")]
public class Blog
{
    [DbColumn("serial8", IsAutoNumber = true, IsPrimaryKey = true)]
    public int BlogId { get; set; }

    // Defining being a child
    [DbChildren]
    public List<Post> Posts { get; } = new();
}

[DbTable("posts")]
public class Post
{
    [DbColumn("serial8", IsAutoNumber = true, IsPrimaryKey = true)]
    public int? PostId { get; set; }

    // Defines the conditions for joining with the parent
    [DbParentRelationColumn("bigint", nameof(Post.Blog.BlogId))]
    public Blog Blog { get; set; } = null!;
}

NOTE: Parent and child association management is within the scope of Model

For example, if there is an instance called blog, the model must ensure that blog.Posts[0].Blog.Equal(blog).

To ensure this relationship, we recommend using an ObservableCollection.

Using ObservableCollection, it can be expressed as follows.

// vanilla
[DbTable("blogs")]
public class Blog
{
    [DbChildren]
    public DirtyCheckableCollection<Post> Posts { get; } = new();

    public Blog()
    {
        Posts.CollectionChanged += Posts_CollectionChanged;
    }

    private void Posts_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            if (e.NewItems == null) return;
            foreach (Post item in e.NewItems)
            {
                item.Blog = this;
            }
        }
    }
}

It is difficult to write the above code without missing anything, so we recommend using PropertyBind. If you use the same library, the above code can be written as follows.

// use PropertyBind
[GeneratePropertyBind(nameof(Posts), nameof(Post.Blog))]
[DbTable("blogs")]
public partial class Blog
{
    [DbChildren]
    public DirtyCheckableCollection<Post> Posts { get; }
}

Index

Although not required, it is also possible to define an index using DbIndexAttribute. Please specify the property name you want to index.

[DbTable("blogs")]
[DbIndex(true, nameof(Url))]
public partial class Blog
{
    [DbColumn("text")]
    public string Url { get; set; } = string.Empty;
}

Convert to definition class

You can obtain a definition class by passing a type with various attributes to the Create method of the DefinitionBuilder class.Please register the table mapping definition in RedOrb using the ObjectRelationMapper.AddTypeHandler method.

using RedOrb.Attributes;
var def = DefinitionBuilder.Create<Blog>();
ObjectRelationMapper.AddTypeHandler(def);

NOTE: Please register only once in the application.

DDL

DDL can be generated from definition classes. However, this function is called internally within the framework, so there is usually no need to call it.

var createTable = def.ToCreateTableCommandText();
var createIndex = def.ToCreateIndexCommandTexts();
create table if not exists blogs (
    blog_id serial8 not null, 
    url text not null, 
    primary key(blog_id)
)
;
create unique index if not exists i1_blogs on blogs (url)
;

Sample

using PropertyBind;
using RedOrb;
using RedOrb.Attributes;

[GeneratePropertyBind(nameof(Posts), nameof(Post.Blog))]
[DbTable("blogs")]
[DbIndex(true, nameof(Url))]
public partial class Blog
{
    [DbColumn("serial8", IsAutoNumber = true, IsPrimaryKey = true)]
    public int BlogId { get; set; }
    [DbColumn("text")]
    public string Url { get; set; } = string.Empty;

    [DbChildren]
    public DirtyCheckableCollection<Post> Posts { get; }
}

[DbTable("posts")]
public partial class Post
{
    [DbColumn("serial8", IsAutoNumber = true, IsPrimaryKey = true)]
    public int PostId { get; set; }
    [DbParentRelationColumn("bigint", nameof(Post.Blog.BlogId))]
    public Blog Blog { get; set; } = null!;
    [DbColumn("text")]
    public string Title { get; set; } = string.Empty;
    [DbColumn("text")]
    public string Content { get; set; } = string.Empty;
}