一開始對這個沒怎么重視铺峭,只知道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
- 使用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": []
}
}
]
}
- 分別查詢?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還在
全文完侧但,希望有幫助