Как загрузить сущность из нескольких таблиц с Dapper? - PullRequest
0 голосов
/ 22 октября 2019

В моем приложении я использую Entites в качестве таблиц в представлении базы данных.

У меня есть OrderEntity, в котором есть такие поля, как ProductEntity, CustomerEntity, затем CustomerEntity имеет такие поля, как AddressEntity и т. Д.

Теперь япопытайтесь заполнить OrderEntity всеми свойствами типа объекта и так далее. Похоже, мне нужно загрузить данные из 8 таблиц.

Я просто не знаю, как это сделать правильно. У меня есть OrderRepository с методом Get, когда я хочу вернуть OrderEntity. Поэтому я должен создать SQL с 7 объединениями, один класс со всеми столбцами из SQL, а затем после выполнения SQL создать вручную OrderEntity и т. Д. В методе Get этого репозитория?

Использовать репозиторий и т. Д. Легко, когда мне нужнополучить / обновить 1 таблицу, но когда модель построена из более чем 1-2 таблиц, мне становится очень тяжело.

Ответы [ 3 ]

1 голос
/ 22 октября 2019

Вариант 1:

Подход, который я использовал, заключается в загрузке каждого отношения по отдельности (для небольших N таблиц). Если у вас 8 таблиц, то 8 запросов предоставят все необходимые вам данные. Вот надуманный пример из 3 таблиц.

public class Person
{
    public int PersonID { get; set; }
    public string PersonName { get; set; }

    public Address[] Addresses { get; set; }
}

public class Address 
{
    public int AddressID { get; set; }
    public int PersonID { get; set; }
    public string AddressLine1 { get; set; }
    public string City{ get; set; }
    public string StateCode { get; set; }
    public string PostalCode { get; set; }

    public Note[] Notes { get; set; }
}

public class Note 
{
    public int AddressID { get; set; }
    public int NoteID { get; set; }
    public string NoteText { get; set; }
}

Вы бы запросили каждую из таблиц.

var people = conn.Query<Person>("select * from Person where ...");
var personIds = people.Select(x => x.PersonID);

var addresses = conn.Query<Address>("select * from Address where PersonID in @PersonIds", new { personIds });
var addressIds = addresses.Select(x => x.AddressID);

var notes = conn.Query<Note>("select * from Note where AddressID in @AddressIds", new { addressIds });

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

// Group addresses by PersonID
var addressesLookup = addresses.ToLookup(x => x.PersonID);
// Group notes by AddressID
var notesLookup = notes.ToLookup(x => x.AddressID);

// Use the lookups above to populate addresses and notes
people.Each(x => x.Addresses = addressesLookup[x.PersonID].ToArray());
addresses.Each(x => x.Notes = notesLookup[x.AddressID].ToArray());

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

Вариант 2:

По следующей ссылке вы можете использовать QueryMultiple.

https://medium.com/dapper-net/handling-multiple-resultsets-4b108a8c5172

Введите следующий код, в котором ваши дочерние запросы должны будут выбрать все записи.

var results = conn.QueryMultiple(@"
    SELECT Id, CompanyId, FirstName, LastName FROM dbo.Users WHERE LastName = 'Smith'; 
    SELECT Id, CompanyName FROM dbo.Companies WHERE CompanyId IN ( SELECT CompanyId FROM dbo.Users WHERE LastName = 'Smith' );
");
var users = results.Read<User>();            
var companies = results.Read<Company>();

Тогда вы исправите отношения, как в Вариант 1 .

0 голосов
/ 25 октября 2019

OK (как указано выше) - пример использования Tuple и Dapper.

Я очень быстро написал это, так что если будут какие-то ошибки, дайте мне знать, и я исправлю. Я на 100% уверен, что это тоже можно оптимизировать!

Используя приведенную выше структуру в качестве примера:

public class Person
{
    public int PersonID { get; set; }
    public string PersonName { get; set; }

    public IEnumerable<Address> Addresses { get; set; }
}

public class Address 
{
    public int AddressID { get; set; }
    public int PersonID { get; set; }
    public string AddressLine1 { get; set; }
    public string City{ get; set; }
    public string StateCode { get; set; }
    public string PostalCode { get; set; }

    public IEnumerable<Note> Notes { get; set; }
}

public class Note 
{
    public int AddressID { get; set; }
    public int NoteID { get; set; }
    public string NoteText { get; set; }
}


string cmdTxt = @"SELECT p.*, a.*, n.* 
    FROM Person p
    LEFT OUTER JOIN Address a ON p.PersonId = a.PersonId
    LEFT OUTER JOIN Note n ON a.AddressId = n.AddressId
    WHERE p.PersonId = @personID";

var results = await conn.QueryAsync<Person,Address,Note,Tuple<Person,Address,Note>>(cmdTxt, 
   map: (p,a,n)=>Tuple.Create((Person)p, (Address)a, (Note)n),
   param: new { personID = 1 });

if(results.Any()) {
    var person = results.First().Item1;   //the person
    var addresses = results.Where(n => n.Item2 != null).Select(n=>n.Item2); //the person's addresses
    var notes = results.Where(n => n.Item3 != null).Select(n=>n.Item3);  //all notes for all addresses
    if(addresses.Any()) {
         person.Addresses = addresses.ToList(); //add the addresses to the person
         foreach(var address in person.Addresses) {
             var address_notes = notes.Where(n=>n.AddressId==address.AddressId).ToList(); //get any notes
             if(address_notes.Any()) {
                 address.Notes = address_notes; //add the notes to the address
             }
         }
    }
}
0 голосов
/ 22 октября 2019

Попробуйте использовать представление в SQL, это действительно упростит задачу.

Представление работает как таблица, также как и более подробные запросы для представления такие же, как и к таблице. То, как вы хотите это сделать, сделает все сложнее, чем должно быть.

...