Есть ли способ сделать что-то подобное? Допустим, у меня есть два класса:
public class Class_A
{
public string Name {get;}
public string Age {get;}
public int Key {get;}
}
public class Class_B
{
public string Doors {get;}
public string Windows {get;}
public int Key {get;}
}
а затем комбинированный класс следующим образом:
public class Class_C
{
public Class_A CA {get;}
public Class_B CB {get;}
}
теперь скажем, я говорю
_connection.Query<Class_C>("A SQL that returns results from JOIN of Clas_A and Class_B")
Тогда он сможет каким-то образом знать, какое поле чему сопоставить. Потому что в противном случае мне придется снова скопировать и вставить поля этих двух классов внутри Class_C, вот так:
public class Class_C
{
public string Name {get;}
public string Age {get;}
public string Doors {get;}
public string Windows {get;}
}
У меня есть один набор результатов: поскольку SQL является результатом JOIN между двумя таблицами, поэтому некоторые данные для этого JOIN поступают из Table_A, которая сопоставляется с Class_A, а некоторые данные поступают из Table_B, которые сопоставляются с Class_B: Таким образом, конечный результат класса - это поля из ОБА Class_A и Class_B, поэтому я надеялся, что если бы у меня был Class_C, который вместо копирования всех полей в нем снова, я мог бы просто сослаться на Class_A и Class_B, и он автоматически расширил бы свойства этих классов.
Как вы связываете A и B в базе данных?
@derloopkat: Просто представьте, что есть какой-то ключ, по которому их можно объединить в SQL. Я также добавил поле «Ключ» в оба примера.
Отношения 1-1?
@Steve: В моем случае да, это 1-1. ЛЕВОЕ СОЕДИНЕНИЕ. лайк Table_A a LEFT JOIN Table_B b on a.Key = b.Key
Не проверялось, но я думаю, вы могли бы написать это:
string cmd = @"select a.Key, a.Name, a.Age,
b.Key, b.Doors, b.Windows
from Class_A a INNER JOIN Class_B b on a.Key = b.Key";
var listOfC = connection.Query<Class_A, Class_B, Class_C>(cmd, (a,b) =>
{
Class_C c = new Class_C();
c.CA = a;
c.CB = b;
return c;
}, splitOn:"Key");
Если вы просто хотите использовать Class_C со всеми свойствами A и B, тогда это просто
string cmd = @"select a.Key, a.Name, a.Age,
b.Doors, b.Windows
from Class_A a INNER JOIN Class_B b on a.Key = b.Key";
var listOfC = connection.Query<Class_C>(cmd);
и Dapper просто сопоставит поля со свойствами Class_C.
Спасибо, Class_C на самом деле не нужен. Я придумал Query<Class_C>
только потому, что в результате JOIN есть поля, доступные как в Class_A, так и в Class_B, и я не смог найти способ использовать синтаксис connection.Query<T>
для моего окончательного результата. Я бы мог пройти connection.Query<dynamic>
, но это ещё хуже! поэтому я надеялся, что каким-то образом можно будет динамически объединить поля этих двух классов.
Каким будет T, зависит от вашего варианта использования. Я думаю, что в отношениях 1-1 хорошо иметь класс, все свойства которого точно соответствуют полям, полученным при объединении.
Да, точно. T похож на тот Class_C, который, как вы сказали, «имеет все свойства, точно соответствующие полям, полученным при объединении». НО, чтобы иметь такой класс, я копирую свойства Class_A и Class_B в Class_C. И я хотел посмотреть, есть ли лучший способ, чем дублировать и копировать эти свойства в Class_C.
Следуя вашим комментариям, для устранения всей избыточности вы можете добавить PropertyB
к классу A
и позволить Dapper связать это свойство с результатом вашего запроса.
public class A
{
public string Name { get; set; }
public string Age { get; set; }
public int Key { get; set; }
public B PropertyB { get; set; }
}
public class B
{
public string Doors { get; set; }
public string Windows { get; set; }
public int Key { get; set; }
}
Запрос:
SELECT * FROM A INNER JOIN B ON A.[Key] = B.[Key];
Результатами являются поля в следующем порядке Name, Age, Key, Doors, Windows, Key
.
Итак, если разделить на Doors
:
A
(Name, Age, Key
)B
(Doors, Windows, Key
).var results = connection.Query<A, B, A>(
sql,
(a, b) =>
{
a.PropertyB = b;
return a;
},
splitOn: "Doors")
.Distinct()
.ToList();
Query<A, B, A>
означает, что слева — это тип A
, справа — это тип B
, а объединенный результат A
генерируется вышеуказанной функцией.
Для получения дополнительной информации см. документацию Dapper Multi-Mapping Result. Имеется поддержка сопоставления «один к одному» и «один ко многим».
Спасибо за объяснение. Но эта часть «левая — это тип A, правая — это тип B, а объединенный результат — это A», то есть вы имеете в виду, что она поместит результаты в класс A? но у класса недостаточно свойств, чтобы вместить все четыре вещи из JOIN. Я неправильно понял это?
A
имеет PropertyB
. Если вы поставите точку останова a.PropertyB = b;
, вы увидите, что она делает. Вы получаете первые 3 поля в A
и последние 3 поля в A.PropertyB
.
В учебнике Dapper они есть invoice.InvoiceDetail = invoiceDetail;
, поэтому вы получаете счет с подробностями счета.
Спасибо чувак. Я сяду читать учебник. Нет спасения от того, чтобы не учиться этому.
Не совсем понятно, но я думаю, вам стоит посмотреть Learndapper.com/dapper-query/selecting-multiple-results