Как вернуть вложенные объекты отношения многие ко многим с помощью автозапроса - PullRequest
2 голосов
/ 19 июня 2019

Допустим, у меня есть 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 в один запрос.

Возможно ли это?

1 Ответ

1 голос
/ 19 июня 2019

Я добавил живой пример, чтобы сделать то, что вы хотите , с которым вы можете играть на Gistlyn .

В OrmLite каждый класс Data Model отображается 1: 1 с базовой таблицей, и естьнет волшебной поддержки запросов M: M, вы должны использовать их в качестве разных таблиц, так как они хранятся в RDBMS.

Кроме того, каждой таблице нужен уникальный первичный идентификатор в OrmLite, который отсутствует в BookAuthor, который я добавил, я также добавил [UniqueConstraint], чтобы исключить дублирующиеся отношения, с этими изменениями получающиеся классывыглядит так:

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; }
}

Затем создайте таблицы и добавьте некоторые примеры данных:

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, вы можете использовать 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);

Поскольку имена свойств соответствуют ссылочным соглашениям , их объединения не нужно указывать явно, поскольку они могут быть неявно выведены.

Это вернет 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

AutoQuery может реализовывать только неявные запросы, которые он может автоматизировать, если вынужно сделатьлюбые пользовательские запросы или проекции, которые вам нужно будет предоставить настраиваемой реализации AutoQuery , поскольку объединения могут быть неявно определены, возможно, вы можете позволить 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() };
    }
}
...