如何return 多对多关系的嵌套对象与autoquery
How to return nested objects of many-to-many relationship with autoquery
假设我有 3 个 类:
public class Book
{
[Autoincrement]
public int Id {get; set;}
public string Title {get; set;}
[Reference]
public list<BookAuthor> BookAuthors {get; set;}
}
public class BookAuthor
{
[ForeignKey(typeof(Book))]
public int BookId {get; set;}
[Reference]
public Book Book {get; set;}
[ForeignKey(typeof(Author))]
public int AuthorId {get; set;}
[Reference]
public Author Author {get; set;}
}
public class Author
{
[Autoincrement]
public int Id {get; set;}
public string Name {get; set;}
}
书籍和作者之间存在多对多关系。
这是我目前正在构建的应用程序的常见问题,我需要将这样的 DTO 提供给前端:
public class BookDto
{
public int Id {get; set;}
public string Title {get; set;}
public list<Author> Authors {get; set;}
}
前端需要嵌入作者。我需要一种在单个查询中将作者嵌套在 DTO 中的方法。
这可能吗?
我已经添加了一个活生生的例子来做你想做的事you can play with on Gistlyn。
在 OrmLite 中,每个数据模型 class 映射 1:1 与底层 table 并且没有对 M:M 查询的神奇支持,您必须将它们用作不同的tables 作为它们存储在 RDBMS 中。
此外,每个 table 在 OrmLite 中都需要一个唯一的主 ID,而我添加的 BookAuthor
中缺少该 ID,我还添加了一个 [UniqueConstraint]
以强制不存在重复关系,经过这些更改,结果 classes 看起来像:
public class Book
{
[AutoIncrement]
public int Id {get; set;}
public string Title {get; set;}
[Reference]
public List<BookAuthor> BookAuthors {get; set;}
}
[UniqueConstraint(nameof(BookId), nameof(AuthorId))]
public class BookAuthor
{
[AutoIncrement] public int Id {get; set;}
[ForeignKey(typeof(Book))]
public int BookId {get; set;}
[ForeignKey(typeof(Author))]
public int AuthorId {get; set;}
}
public class Author
{
[AutoIncrement]
public int Id {get; set;}
public string Name {get; set;}
}
public class BookDto
{
public int Id { get; set; }
public string Title { get; set; }
public List<Author> Authors { get; set; }
}
然后创建 tables 并添加一些示例数据:
db.CreateTable<Book>();
db.CreateTable<Author>();
db.CreateTable<BookAuthor>();
var book1Id = db.Insert(new Book { Title = "Book 1" }, selectIdentity:true);
var book2Id = db.Insert(new Book { Title = "Book 2" }, selectIdentity:true);
var book3Id = db.Insert(new Book { Title = "Book 3" }, selectIdentity:true);
var authorAId = db.Insert(new Author { Name = "Author A" }, selectIdentity:true);
var authorBId = db.Insert(new Author { Name = "Author B" }, selectIdentity:true);
db.Insert(new BookAuthor { BookId = 1, AuthorId = 1 });
db.Insert(new BookAuthor { BookId = 1, AuthorId = 2 });
db.Insert(new BookAuthor { BookId = 2, AuthorId = 2 });
db.Insert(new BookAuthor { BookId = 3, AuthorId = 2 });
然后在 OrmLite 的单个查询中 select 多个 tables 你可以使用 SelectMulti,例如:
var q = db.From<Book>()
.Join<BookAuthor>()
.Join<BookAuthor,Author>()
.Select<Book,Author>((b,a) => new { b, a });
var results = db.SelectMulti<Book,Author>(q);
作为 属性 名称 follows the reference conventions 它们的连接不需要显式指定,因为它们可以隐式推断。
这将 return 一个 List<Tuple<Book,Author>>
然后您可以使用字典将所有作者与他们的书拼接起来:
var booksMap = new Dictionary<int,BookDto>();
results.Each(t => {
if (!booksMap.TryGetValue(t.Item1.Id, out var dto))
booksMap[t.Item1.Id] = dto = t.Item1.ConvertTo<BookDto>();
if (dto.Authors == null)
dto.Authors = new List<Author>();
dto.Authors.Add(t.Item2);
});
我们可以从字典值中获取图书列表:
var dtos = booksMap.Values;
dtos.PrintDump();
书籍中填入其作者并打印出来的地方:
[
{
Id: 1,
Title: Book 1,
Authors:
[
{
Id: 1,
Name: Author A
},
{
Id: 2,
Name: Author B
}
]
},
{
Id: 2,
Title: Book 2,
Authors:
[
{
Id: 2,
Name: Author B
}
]
},
{
Id: 3,
Title: Book 3,
Authors:
[
{
Id: 2,
Name: Author B
}
]
}
]
自动查询
AutoQuery can only implement implicit queries that it can automate, if you need to do any custom queries or projections you would need to provide a custom AutoQuery implementation,因为可以隐式推断连接,所以您可以让 AutoQuery 构造连接查询,这样您只需要自己提供自定义 Select()
投影和映射,例如:
[Route("/books/query")]
public class QueryBooks : QueryDb<Book,BookDto>,
IJoin<Book,BookAuthor>,
IJoin<BookAuthor,Author> {}
public class MyQueryServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
//Override with custom implementation
public object Any(QueryBooks query)
{
var q = AutoQuery.CreateQuery(query, base.Request)
.Select<Book,Author>((b,a) => new { b, a });
var results = db.SelectMulti<Book,Author>(q);
var booksMap = new Dictionary<int,BookDto>();
results.Each(t => {
if (!booksMap.TryGetValue(t.Item1.Id, out var dto))
booksMap[t.Item1.Id] = dto = t.Item1.ConvertTo<BookDto>();
if (dto.Authors == null)
dto.Authors = new List<Author>();
dto.Authors.Add(t.Item2);
});
return new QueryResponse<BookDto> { Results = booksMap.Values.ToList() };
}
}
假设我有 3 个 类:
public class Book
{
[Autoincrement]
public int Id {get; set;}
public string Title {get; set;}
[Reference]
public list<BookAuthor> BookAuthors {get; set;}
}
public class BookAuthor
{
[ForeignKey(typeof(Book))]
public int BookId {get; set;}
[Reference]
public Book Book {get; set;}
[ForeignKey(typeof(Author))]
public int AuthorId {get; set;}
[Reference]
public Author Author {get; set;}
}
public class Author
{
[Autoincrement]
public int Id {get; set;}
public string Name {get; set;}
}
书籍和作者之间存在多对多关系。
这是我目前正在构建的应用程序的常见问题,我需要将这样的 DTO 提供给前端:
public class BookDto
{
public int Id {get; set;}
public string Title {get; set;}
public list<Author> Authors {get; set;}
}
前端需要嵌入作者。我需要一种在单个查询中将作者嵌套在 DTO 中的方法。
这可能吗?
我已经添加了一个活生生的例子来做你想做的事you can play with on Gistlyn。
在 OrmLite 中,每个数据模型 class 映射 1:1 与底层 table 并且没有对 M:M 查询的神奇支持,您必须将它们用作不同的tables 作为它们存储在 RDBMS 中。
此外,每个 table 在 OrmLite 中都需要一个唯一的主 ID,而我添加的 BookAuthor
中缺少该 ID,我还添加了一个 [UniqueConstraint]
以强制不存在重复关系,经过这些更改,结果 classes 看起来像:
public class Book
{
[AutoIncrement]
public int Id {get; set;}
public string Title {get; set;}
[Reference]
public List<BookAuthor> BookAuthors {get; set;}
}
[UniqueConstraint(nameof(BookId), nameof(AuthorId))]
public class BookAuthor
{
[AutoIncrement] public int Id {get; set;}
[ForeignKey(typeof(Book))]
public int BookId {get; set;}
[ForeignKey(typeof(Author))]
public int AuthorId {get; set;}
}
public class Author
{
[AutoIncrement]
public int Id {get; set;}
public string Name {get; set;}
}
public class BookDto
{
public int Id { get; set; }
public string Title { get; set; }
public List<Author> Authors { get; set; }
}
然后创建 tables 并添加一些示例数据:
db.CreateTable<Book>();
db.CreateTable<Author>();
db.CreateTable<BookAuthor>();
var book1Id = db.Insert(new Book { Title = "Book 1" }, selectIdentity:true);
var book2Id = db.Insert(new Book { Title = "Book 2" }, selectIdentity:true);
var book3Id = db.Insert(new Book { Title = "Book 3" }, selectIdentity:true);
var authorAId = db.Insert(new Author { Name = "Author A" }, selectIdentity:true);
var authorBId = db.Insert(new Author { Name = "Author B" }, selectIdentity:true);
db.Insert(new BookAuthor { BookId = 1, AuthorId = 1 });
db.Insert(new BookAuthor { BookId = 1, AuthorId = 2 });
db.Insert(new BookAuthor { BookId = 2, AuthorId = 2 });
db.Insert(new BookAuthor { BookId = 3, AuthorId = 2 });
然后在 OrmLite 的单个查询中 select 多个 tables 你可以使用 SelectMulti,例如:
var q = db.From<Book>()
.Join<BookAuthor>()
.Join<BookAuthor,Author>()
.Select<Book,Author>((b,a) => new { b, a });
var results = db.SelectMulti<Book,Author>(q);
作为 属性 名称 follows the reference conventions 它们的连接不需要显式指定,因为它们可以隐式推断。
这将 return 一个 List<Tuple<Book,Author>>
然后您可以使用字典将所有作者与他们的书拼接起来:
var booksMap = new Dictionary<int,BookDto>();
results.Each(t => {
if (!booksMap.TryGetValue(t.Item1.Id, out var dto))
booksMap[t.Item1.Id] = dto = t.Item1.ConvertTo<BookDto>();
if (dto.Authors == null)
dto.Authors = new List<Author>();
dto.Authors.Add(t.Item2);
});
我们可以从字典值中获取图书列表:
var dtos = booksMap.Values;
dtos.PrintDump();
书籍中填入其作者并打印出来的地方:
[
{
Id: 1,
Title: Book 1,
Authors:
[
{
Id: 1,
Name: Author A
},
{
Id: 2,
Name: Author B
}
]
},
{
Id: 2,
Title: Book 2,
Authors:
[
{
Id: 2,
Name: Author B
}
]
},
{
Id: 3,
Title: Book 3,
Authors:
[
{
Id: 2,
Name: Author B
}
]
}
]
自动查询
AutoQuery can only implement implicit queries that it can automate, if you need to do any custom queries or projections you would need to provide a custom AutoQuery implementation,因为可以隐式推断连接,所以您可以让 AutoQuery 构造连接查询,这样您只需要自己提供自定义 Select()
投影和映射,例如:
[Route("/books/query")]
public class QueryBooks : QueryDb<Book,BookDto>,
IJoin<Book,BookAuthor>,
IJoin<BookAuthor,Author> {}
public class MyQueryServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
//Override with custom implementation
public object Any(QueryBooks query)
{
var q = AutoQuery.CreateQuery(query, base.Request)
.Select<Book,Author>((b,a) => new { b, a });
var results = db.SelectMulti<Book,Author>(q);
var booksMap = new Dictionary<int,BookDto>();
results.Each(t => {
if (!booksMap.TryGetValue(t.Item1.Id, out var dto))
booksMap[t.Item1.Id] = dto = t.Item1.ConvertTo<BookDto>();
if (dto.Authors == null)
dto.Authors = new List<Author>();
dto.Authors.Add(t.Item2);
});
return new QueryResponse<BookDto> { Results = booksMap.Values.ToList() };
}
}