Cascade saving FreeSql navigation attributes

EDITORIAL

Support .net framework under FreeSql a section .net platform 4.5 + ,. net core of open source ORM 2.1+. Unit testing more than 3100, is attracting new developers, there is life developed more than.

And EFCore, we also have to navigate the object, OneToOne support [] (one to one), ManyToOne [] (many), OneToMany [] (one to many), [ParentChild] (father and son), ManyToMany [] (multiple pairs multi), or the configuration may be agreed between the entities associated with manual configuration may be used provided the associated fluent api.

When cascade-save feature to save the object can be achieved, it OneToMany [], [] navigation ManyToMany set of attributes also be kept together, this document describes the mechanism implemented to prevent misuse.

Mechanism rules

[] Under many models can be saved when the stored attributes of the entity associated set level. For security reasons we did not use to do a complete contrast, achieved only add or update a set of attributes of an entity, it will not delete the data collection entity attributes.

A complete contrast to the function was too dangerous to use, imagine the following scene:

  • Save time, property of an entity set is empty, how to operate? Delete all records?
  • Save time, due to the database record very much, so just want to save part of the data sub-table, or just add, how to operate?

[] Under-many model, we save on the middle of the table is a complete contrast operation, the operation of the external entities only for new (note not updated)

  • When the property set is empty, remove all their associated data (intermediate table)
  • When the property is not empty, should be removed and added to the associated data (intermediate sheet) complete contrast, the calculated database record exists

Opening and closing function

IFreeSql fsql = new FreeSql.FreeSqlBuilder()

    .UseConnectionString(FreeSql.DataType.Sqlite, "Data Source=|DataDirectory|/document22.db;Pooling=true;Max Pool Size=10")

    .UseAutoSyncStructure(true) //自动同步结构到数据库
    .UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText)) //监听SQL命令对象,在执行后
    .Build();

Use FreeSqlBuilder create good IFreeSql objects, cascade save function is enabled by default.

Global Close:

fsql.SetDbContextOptions(opt => opt.EnableAddOrUpdateNavigateList = false);

Partial closure:

var repo = fsql.GetRepository<T>();
repo.DbContextOptions.EnableAddOrUpdateNavigateList = false;

To-many (OneToMany) code testing

To facilitate the display, the following is a ParentChild relationship, in fact, he is OneToMany, but their own point to themselves.

[Table(Name = "EAUNL_OTMP_CT")]
class CagetoryParent
{
    public Guid Id { get; set; }
    public string Name { get; set; }

    public Guid ParentId { get; set; }
    [Navigate("ParentId")]
    public List<CagetoryParent> Childs { get; set; }
}

Initialization test data:

var cts = new[] {
    new CagetoryParent
    {
        Name = "分类1",
        Childs = new List<CagetoryParent>(new[]
        {
            new CagetoryParent { Name = "分类1_1" },
            new CagetoryParent { Name = "分类1_2" },
            new CagetoryParent { Name = "分类1_3" }
        })
    },
    new CagetoryParent
    {
        Name = "分类2",
        Childs = new List<CagetoryParent>(new[]
        {
            new CagetoryParent { Name = "分类2_1" },
            new CagetoryParent { Name = "分类2_2" }
        })
    }
};

1, the implementation of bulk insert:

var repo = g.sqlite.GetRepository<CagetoryParent>();
repo.Insert(cts);

When the initial execution of the method, it will automatically create database tables to perform the operation. If the table exists, comparison is performed, if no change no operation is performed.

After breakpoint debugging, you can see the console output SQL content:

INSERT INTO "EAUNL_OTMP_CT"("Id", "Name", "ParentId") VALUES('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f', '分类1', '00000000-0000-0000-0000-000000000000'), ('5d90afcb-ed57-f6f4-0082-cb6c5b531b3e', '分类2', '00000000-0000-0000-0000-000000000000')

INSERT INTO "EAUNL_OTMP_CT"("Id", "Name", "ParentId") VALUES('5d90afcb-ed57-f6f4-0082-cb6d0c1c5f1a', '分类1_1', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb6e74bd8eef', '分类1_2', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb6f6267cc5f', '分类1_3', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb7057c41d46', '分类2_1', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'), ('5d90afcb-ed57-f6f4-0082-cb7156e0375e', '分类2_2', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e')

2, modify the test batch:

cts[0].Name = "分类11";
cts[0].Childs.Clear();
cts[1].Name = "分类22";
cts[1].Childs.Clear();
repo.Update(cts);

SQL console output to see the contents:

UPDATE "EAUNL_OTMP_CT" SET "Name" = CASE "Id" 
WHEN '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f' THEN '分类11' 
WHEN '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e' THEN '分类22' END 
WHERE ("Id" IN ('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f','5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'))

Childs.Clear performed, but no console output delete a subset of the statement, did not do a complete description of Contrast

3, a subset of table data already exists, continue to add data

cts[0].Name = "分类111";
cts[0].Childs.Clear();
cts[0].Childs.Add(new CagetoryParent { Name = "分类1_33" });
cts[1].Name = "分类222";
cts[1].Childs.Clear();
cts[1].Childs.Add(new CagetoryParent { Name = "分类2_22" });
repo.Update(cts);

SQL console output to see the contents:

UPDATE "EAUNL_OTMP_CT" SET "Name" = CASE "Id" 
WHEN '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f' THEN '分类111' 
WHEN '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e' THEN '分类222' END 
WHERE ("Id" IN ('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f','5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'))

INSERT INTO "EAUNL_OTMP_CT"("Id", "Name", "ParentId") VALUES('5d90afe8-ed57-f6f4-0082-cb725df546ea', '分类1_33', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afe8-ed57-f6f4-0082-cb7338a6214c', '分类2_22', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e')

Once again proved [many] (OneToMany) will not make a complete contrast, only add or update, add test data when using it to simplify a lot of code.

To-many (ManyToMany) code testing

Below we created three classes, Song category for the body, Tag external class, SongTag for the middle class association data, using the naming convention way navigation relationship.

[Table(Name = "EAUNL_MTM_SONG")]
class Song
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public List<Tag> Tags { get; set; }
}
[Table(Name = "EAUNL_MTM_TAG")]
class Tag
{
    public Guid Id { get; set; }
    public string TagName { get; set; }
    public List<Song> Songs { get; set; }
}
[Table(Name = "EAUNL_MTM_SONGTAG")]
class SongTag
{
    public Guid SongId { get; set; }
    public Song Song { get; set; }
    public Guid TagId { get; set; }
    public Tag Tag { get; set; }
}

Initialization test data:

var tags = new[] {
    new Tag { TagName = "流行" },
    new Tag { TagName = "80后" },
    new Tag { TagName = "00后" },
    new Tag { TagName = "摇滚" }
};
var ss = new[]
{
    new Song
    {
        Name = "爱你一万年.mp3",
        Tags = new List<Tag>(new[]
        {
            tags[0], tags[1]
        })
    },
    new Song
    {
        Name = "李白.mp3",
        Tags = new List<Tag>(new[]
        {
            tags[0], tags[2]
        })
    }
};

1, the implementation of bulk insert:

var repo = g.sqlite.GetRepository<Song>();
repo.Insert(ss);

When the initial execution of the method, it will automatically create database tables to perform the operation. If the table exists, comparison is performed, if no change no operation is performed.

After breakpoint debugging, you can see the console output SQL content:

INSERT INTO "EAUNL_MTM_SONG"("Id", "Name") VALUES('5d90fdb3-6a6b-2c58-00c8-37974177440d', '爱你一万年.mp3'), ('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '李白.mp3')

INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90fdb7-6a6b-2c58-00c8-37991ead4f05', '流行'), ('5d90fdbd-6a6b-2c58-00c8-379a0432a09c', '80后')

INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37974177440d', '5d90fdb7-6a6b-2c58-00c8-37991ead4f05'), ('5d90fdb3-6a6b-2c58-00c8-37974177440d', '5d90fdbd-6a6b-2c58-00c8-379a0432a09c')

INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90fdcc-6a6b-2c58-00c8-379b5af59d25', '00后')

INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90fdb7-6a6b-2c58-00c8-37991ead4f05'), ('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90fdcc-6a6b-2c58-00c8-379b5af59d25')

2, the test batch update, and the intermediate data table has changed

ss[0].Name = "爱你一万年.mp5";
ss[0].Tags.Clear();
ss[0].Tags.Add(tags[0]);
ss[1].Name = "李白.mp5";
ss[1].Tags.Clear();
ss[1].Tags.Add(tags[3]);
repo.Update(ss);

SQL console output to see the contents:

UPDATE "EAUNL_MTM_SONG" SET "Name" = CASE "Id" 
WHEN '5d90fdb3-6a6b-2c58-00c8-37974177440d' THEN '爱你一万年.mp5' 
WHEN '5d90fdb3-6a6b-2c58-00c8-37987f29b197' THEN '李白.mp5' END 
WHERE ("Id" IN ('5d90fdb3-6a6b-2c58-00c8-37974177440d','5d90fdb3-6a6b-2c58-00c8-37987f29b197'))

SELECT a."SongId", a."TagId" 
FROM "EAUNL_MTM_SONGTAG" a 
WHERE (a."SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d')

DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d' AND "TagId" = '5d90fdbd-6a6b-2c58-00c8-379a0432a09c')

INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90febd-6a6b-2c58-00c8-379c21acfc72', '摇滚')

SELECT a."SongId", a."TagId" 
FROM "EAUNL_MTM_SONGTAG" a 
WHERE (a."SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197')

DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197' AND "TagId" = '5d90fdb7-6a6b-2c58-00c8-37991ead4f05' OR "SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197' AND "TagId" = '5d90fdcc-6a6b-2c58-00c8-379b5af59d25')

INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90febd-6a6b-2c58-00c8-379c21acfc72')

The implementation process is as follows:

  • The first step, batch update song data
  • The second step, since the song is an update operation, so you need to find out the associated data song
  • The third step, delete the song associated data (tags [0] except), because tags [0] is save some of this data, straightforward to say is to remove all non-associated data in this saved
  • 4. Add tags [3] Rock external data, since it does not exist outside the table
  • The fifth step, the second step of the same
  • The sixth step, the third step is the same
  • A seventh step, the intermediate insert table data, Bai .mp5 associated with the rock

Why are there so many steps it? The reason is that song test data is two, double, and if a single record is about 4-5, depending on whether you need to add new related data.

3, the test data associated empty

ss[0].Name = "爱你一万年.mp4";
ss[0].Tags.Clear();
ss[1].Name = "李白.mp4";
ss[1].Tags.Clear();
repo.Update(ss);

SQL console output to see the contents:

DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d')

DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197')

UPDATE "EAUNL_MTM_SONG" SET "Name" = CASE "Id" 
WHEN '5d90fdb3-6a6b-2c58-00c8-37974177440d' THEN '爱你一万年.mp4' 
WHEN '5d90fdb3-6a6b-2c58-00c8-37987f29b197' THEN '李白.mp4' END 
WHERE ("Id" IN ('5d90fdb3-6a6b-2c58-00c8-37974177440d','5d90fdb3-6a6b-2c58-00c8-37987f29b197'))

Once again proved that the ManyToMany] [(many) model, a complete contrast to the intermediate table operation, insert only the outer table is not updated.

Object navigation

In addition to the cascade-save feature, designed primarily for the purpose of navigation objects quick little point interspersed between entities in order to perform query lambda expression.

How to customize navigation relationship?

//导航属性,OneToMany
[Navigate("song_id")]
public virtual List<song_tag> Obj_song_tag { get; set; }

//导航属性,ManyToOne/OneToOne
[Navigate("song_id")]
public virtual Song Obj_song { get; set; }

//导航属性,ManyToMany
[Navigate(ManyToMany = typeof(tag_song))]
public virtual List<tag> tags { get; set; }
  • You may agree, may not be agreed;
  • No agreement, the associated characteristics are required for Navigate;
  • Unrelated, the query can specify On condition, LeftJoin (a => a.Parent.Id == a.ParentId);
  • Linked directly use the navigation objects on the line, On the conditions are appended automatically;

You can also use FluentApi set the navigation external relations:

fsql.CodeFirst.ConfigEntity<实体类>(a => a
    .Navigate(b => b.roles, null, typeof(多对多中间实体类))
    .Navigate(b => b.users, "uid")
);

Priority properties> FluentApi

Written in the last

FreeSql release has been 10 months, the official New Year's Day will be released 1.0 version, I hope in the future can become the community under the wheels .net awesome, I can be considered a little more than a decade worthwhile contribution to the .net never betray it.

FreeSql hope getting better and better,

Original .net core getting better and better! (Although the 3.0 upgrade many people turn out of the car, there is in the hearts of those feelings, turned the car is at most only a few curse, Ma Wan then have to use it)

Tutorial Address: "FreeSql Novice tutorial series has been published in cnblogs"

Source Address: https://github.com/2881099

Guess you like

Origin www.cnblogs.com/kellynic/p/11610724.html