EFCore many-to-many 的CRUD

一開始對這個沒怎么重視铺峭,只知道efcore的m2m需要中間表逼友,今天做到這塊的時候才發(fā)現(xiàn)不會寫了...查了很多資料榴嗅,才找到正確寫法拇惋。趕緊記錄一下
首先模型:

    public class Book
    {
        public int BookId { get; set; } 
        public string Title { get; set; }
        public string Description { get; set; } 

       public ICollection<BookAuthor> Authors { get; set; }
    }
    public class Author
    {
        public int AuthorId { get; set; }
        public string Name { get; set; }

        public virtual ICollection<BookAuthor> Books { get; set; }
    }

    public class BookAuthor
    {
        public int BookId { get; set; }       
        public int AuthorId { get; set; } 
    
        public Book Book { get; set; }      
        public Author Author { get; set; }    
    }

    public class EfCoreContext : DbContext
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Author> Authors { get; set; }
        public DbSet<BookAuthor> BookAuthors { get; set; }


        public EfCoreContext(DbContextOptions<EfCoreContext> options): base(options) { }

        protected override void   OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<BookAuthor>().HasKey(x => new { x.BookId, x.AuthorId });         
        }
    }

C

這個最簡單周偎,直接上代碼

        [HttpPost]
        public IActionResult Create(BookCreateDTO bookDTO )
        {
            if (bookDTO.Author!=null)
            {
                bookDTO.Book.Authors = new List<BookAuthor>
                {
                    new BookAuthor
                    {
                        Author = bookDTO.Author,
                        Book = bookDTO.Book,
                        Order=0
                    }
                };
            }
          
            _context.Books.Add(bookDTO.Book);
            _context.SaveChanges();
            return Ok();
        }
// BookCreateDTO
    public class BookCreateDTO
    {
        public Book Book { get; set; }
        public Author Author { get; set; }
    }

其實這個Author不要也行,等用戶創(chuàng)建完畢后再添加也可以撑帖。

R

  1. 使用eagar loading的查詢(Include)
[HttpGet]
public IActionResult List()
{
    return Ok(_context.Books.Include(x => x.Authors).ThenInclude(y => y.Author).ToList());
}
[HttpGet("{id}")]
public IActionResult One(int id)
{
    return Ok(_context.Books.Include(x => x.Authors).ThenInclude(y => y.Author).FirstOrDefault(x=>x.BookId == id));
}

查詢結(jié)果:

{
    "bookId": 2,
    "title": "newBook2",
    "description": null,
    "publishedOn": "0001-01-01T00:00:00",
    "publisher": null,
    "price": 0,
    "imageUrl": null,
    "softDeleted": false,
    "authorsLink": [
        {
            "bookId": 2,
            "authorId": 1,
            "order": 0,
            "author": {
                "authorId": 1,
                "name": "Monodev2",
                "bookAuthors": []
            }
        },
        {
            "bookId": 2,
            "authorId": 2,
            "order": 0,
            "author": {
                "authorId": 2,
                "name": "Monodev2",
                "bookAuthors": []
            }
        }
    ]
}
  1. 分別查詢?nèi)缓蠼M合
        [HttpGet("{id}")]
        public IActionResult Detail(int id)
        {
            List<Author> authors = _context.BookAuthors
                .Include(x => x.Author)
                .Where(c => c.BookId == id)
                .Select(x=>x.Author)
                .ToList();
            Book book = _context.Books.Single(x => x.BookId == id);
            var result =  new
            {
                book=book,
                authors = authors
            };
            return Ok(result);
        }

上面方法關(guān)鍵在于Select這個地方蓉坎,因為_context.BookAuthors.Include(x => x.Author).Where(c => c.BookId == id)這樣查出來是BookAuthor對象,直接返回客戶端數(shù)據(jù)就會很臟胡嘿,并且有嵌套蛉艾,只選擇author更符合前端消費習(xí)慣

{
    "book": {
        "bookId": 2,
        "title": "newBook2",
        "description": null,
        "publishedOn": "0001-01-01T00:00:00",
        "publisher": null,
        "price": 0,
        "imageUrl": null,
        "softDeleted": false,
        "authors": null
    },
    "authors": [
        {
            "authorId": 1,
            "name": "Monodev2",
            "books": null
        },
        {
            "authorId": 2,
            "name": "Monodev2",
            "books": null
        }
    ]
}

上面兩種方法比較明顯第二種方法更好。

U

更新是比較麻煩的衷敌,首先勿侯,需要寫一個靜態(tài)擴展:

    public static class Extensions
    { 

        public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
        {
            db.Set<T>().RemoveRange(currentItems.ExceptThat(newItems, getKey));
            db.Set<T>().AddRange(newItems.ExceptThat(currentItems, getKey));
        }

        private static IEnumerable<T> ExceptThat<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
        {
            return items
                .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
                .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
                .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
                .Select(t => t.t.item);
        }
    }

然后,定義更新模型:

    public class BookUpdateDTO
    {
        public Book Book { get; set; }
        public List<int> Authors { get; set; }
    }

然后缴罗,在Controller中這樣寫:

        [HttpPut]
        public IActionResult Update(BookUpdateDTO bookUpdateDTO)
        {
            var model = _context.Books.Include(x => x.Authors).FirstOrDefault(x => x.BookId == bookUpdateDTO.Book.BookId);
            _context.TryUpdateManyToMany(model.Authors, bookUpdateDTO.Authors
                .Select(x => new BookAuthor
                {
                    AuthorId = x,
                    BookId = bookUpdateDTO.Book.BookId
                }),x=>x.AuthorId);
            _context.SaveChanges();
            return Ok();
        }

本質(zhì)上助琐,就是遍歷傳進的model中的MM,然后用靜態(tài)擴展中的方法進行更新面氓。

D

        [HttpDelete("{id}")]
        public IActionResult Remove(int id)
        {
            var model = _context.Books.FirstOrDefault(x => x.BookId == id);
            if (model != null)
            {
                _context.Books.Remove(model);
                _context.SaveChanges();
                return Ok();
            }
            return NoContent();
        }

EFCore 默認即為級聯(lián)刪除弓柱,所以關(guān)聯(lián)表中bookid==2的數(shù)據(jù)將被一并刪除,當(dāng)然authors不會被刪除:


bookid==2的數(shù)據(jù)已被刪除

id=2的author還在

全文完侧但,希望有幫助

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矢空,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子禀横,更是在濱河造成了極大的恐慌屁药,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柏锄,死亡現(xiàn)場離奇詭異酿箭,居然都是意外死亡复亏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門缭嫡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缔御,“玉大人,你說我怎么就攤上這事妇蛀「唬” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵评架,是天一觀的道長眷茁。 經(jīng)常有香客問我,道長纵诞,這世上最難降的妖魔是什么上祈? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮浙芙,結(jié)果婚禮上登刺,老公的妹妹穿的比我還像新娘。我一直安慰自己嗡呼,他們只是感情好纸俭,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晤锥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪廊宪。 梳的紋絲不亂的頭發(fā)上矾瘾,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音箭启,去河邊找鬼壕翩。 笑死,一個胖子當(dāng)著我的面吹牛傅寡,可吹牛的內(nèi)容都是我干的放妈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荐操,長吁一口氣:“原來是場噩夢啊……” “哼芜抒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起托启,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宅倒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后屯耸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拐迁,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蹭劈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了线召。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铺韧。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖缓淹,靈堂內(nèi)的尸體忽然破棺而出哈打,到底是詐尸還是另有隱情,我是刑警寧澤割卖,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布前酿,位于F島的核電站,受9級特大地震影響鹏溯,放射性物質(zhì)發(fā)生泄漏罢维。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一丙挽、第九天 我趴在偏房一處隱蔽的房頂上張望肺孵。 院中可真熱鬧,春花似錦颜阐、人聲如沸平窘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瑰艘。三九已至,卻和暖如春肤舞,著一層夾襖步出監(jiān)牢的瞬間紫新,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工李剖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芒率,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓篙顺,卻偏偏與公主長得像偶芍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子德玫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354