#c# #asp.net-mvc #entity-framework #linq
#c# #asp.net-mvc #entity-framework #linq
Вопрос:
Есть 3 объекта, допустим, они представлены таким образом:
**1 - entity**
class A
int id ;
int Name;
[Foreign key]
int id_B;
List C;
**2 - entity**
class B
int id ;
int Name;
List A;
**3 - entity**
class C
int id;
int Name;
[Foreign Key]
int id_A;
создан объект DTO (все то же самое, только без внешних ключей)
1
class ADTO
int id ;
int Name;
List C;
2
class BDTO
int id ;
int Name;
List A;
3
class CDTO
int id;
int Name;
Теперь запрос выглядит следующим образом:
var quer = (await _context.A
.Include(b => b.B)
.Include(c => c.C)
.Where(u => u.Id == 1).ToListAsync())
.Select(a => new ADto
{
Id = a.Id,
//How to get information about entity B here by converting to DTO
C = a.C.Select(cdto => new CDTO{ Id = cdto.Id, Name = cdto.Name}).ToList(),
});
Как получить информацию о сущности B здесь путем преобразования в DTO?
Комментарии:
1. Одно слово: Automapper.
Ответ №1:
Если вы запрашиваете «A» в качестве объекта верхнего уровня, то я полагаю, что вам просто не хватает свойства навигации, связанного с ним «B». (Поскольку он содержит B_Id FK)
1 — объект
public class A
{
public int id { get; set; }
public string Name { get; set; }
[ForeignKey("B")]
public int id_B { get; set; }
public virtual B B { get; set; }
public virtual ICollection<C> Cs { get; set;} = new List<C>();
}
Затем, когда вы проецируете свои объекты в DTO с помощью Select
:
var query = (await _context.A
.Where(a => a.Id == 1)
.Select(a => new ADto
{
Id = a.Id,
B = new BDTO { Id = a.B.Id /* ... */ },
Cs = a.Cs.Select(c => new CDTO{ Id = c.Id, Name = c.Name}).ToList(),
}).Single();
Обратите внимание, что при использовании .Select
вам не нужно использовать .Include
для ссылки на связанные объекты, это используется только для быстрой загрузки связанных объектов, где вы хотите вернуть граф объектов. (Например, при чтении объектов для обновления значений из DTO) Кроме того, будьте осторожны с использованием любых ToList
операций перед использованием Select
, поскольку это загрузит объекты в память перед применением таких вещей, как фильтры, и сведет на нет оптимизацию запросов для заполнения только того, что Select
необходимо.
});
Комментарии:
1. Спасибо, но у меня есть свойство навигации в моем классе A. Если я использую: B = new BDTO { Id = a.B.Id /* … */ }, я конвертирую свой BDTO в B …. это неправильно.
2. Я сделал это. Спасибо)
Ответ №2:
Обычно я бы предложил вам реализовать интерфейс, который предоставляется в конструкторе результирующего объекта
итак:
public interface IDbOjbect{
int Id {get;set;}
string Name{get;set;}
}
а затем на вашем объекте DTO
public Class DtoObject {
public DtoOjbect(IDbOjbect source)
{
//Mapping done here.
}
}
Потому что тогда вы можете реализовать интерфейс для любого объекта уровня сохраняемости, и сопоставление все равно будет работать.
Потому что тогда запрос linq просто:
DbOjbectList.Select(x => new DtoObject(x));
при условии, что DtoOjbect реализует интерфейс.
ваш C будет выглядеть так:
public partial class C {
public int id {get;set;}
public string Name {get;set;}
}
public partial class C : IDbOjbect {
}
и ваш CDTO будет выглядеть как:
public Class CDTO{
public int Id {get;set;}
public string Name {get;set;}
public CDTO(IDbOjbect source)
{
Id = source.Id;
Name = source.name;
}
}
Хотите иметь возможность создавать DTO из B?
Реализовать IDbOjbect на вашем B
используя
public partial class B {
public int id {get;set;}
public string Name {get;set;}
}
public partial class B : IDbOjbect {
}
и теперь любой C или B может быть преобразован в CDTO.
Самое приятное, что вы можете создать универсальный метод для ваших B и C, использовать ключевое слово «Where» после вашего общего определения, а затем использовать интерфейс в качестве типа, теперь вы можете создать единый метод, который делает то же самое на основе того, что имеет реализацию интерфейса, и это также будет работать для A, если вы реализуете интерфейс на A.
Без дальнейших изменений.
Итак, теперь, когда вы задаете вопросы, ваш первоначальный вопрос не соответствует, давайте расширим.
Допустим, у вас есть объект ResumeInfo, который доступен только B.
Затем вы используете шаблон NullPointer вместе с принципом разделения интерфейса.
Итак, вы создаете интерфейс в своем классе resumeInfo
Пример:
public interface IResumeInfo
{
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
}
Затем в вашем объекте ResumeInfo:
public partial class ResumeInfo
{
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
}
public partial class ResumeInfo : IResumeInfo
{
}
Тогда допустим, вам нужен один объект DTO:
public class DTOUserAndResume
{
public int id {get;set;}
public string Name {get;set;}
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
public DTOUserAndResume(IDbOjbect source, IResumeInfo resumeInfo)
{
Id = source.Id;
Name = source.name;
PlaceOfEmployment = resumeInfo.PlaceOfEmployment;
StartOfEmployment = resumeInfo.StartOfEmployment ;
EndOfEmployment = resumeInfo.EndOfEmployment ;
}
}
Теперь о B? Я думаю, вы сказали, что у вас есть данные возобновления, но не на C?
вы реализуете IResumeInfo на обоих, но на B вы просто получаете любые данные, которые есть, но на C, у которого нет данных? Шаблон NullOjbect.
Реализуйте интерфейс, но сделайте так, чтобы он ничего не возвращал.
Таким образом, PlaceOfEmployment всегда «» или Null. Начальные данные всегда 1900-01-01 00:00:00 или что угодно, что вы хотите, чтобы «ничего» не было в объекте, не обнуляемом, и null в конце занятости.
Итак, вы просто утверждаете, что данные эквивалентны несуществующему набору данных, потому что у него нет набора данных для предоставления.
Но вам не нужно создавать новый DTO, вы можете просто обновить конструктор на CDTO, он также будет работать нормально. Это может просто немного запутать в отношении именования и прочего.
Это должно привести к вызову, который выглядит как:
C = a.C.Select(cdto => new CDTO{cdto, cdto.ResumeInfo}).ToList();
Комментарии:
1. и если реализовано моим методом?
2. C = a.C.Выберите(cdto => новый CDTO{ Id = cdto. Идентификатор, имя = cdto. Имя}). ToList(), преобразованный в: C = a.C.Select(cdto => новый CDTO(cdto) ). ToList(); При условии, что C реализует IDbOjbect, а CDTO принимает IDbOjbect в качестве параметра в своем конструкторе. Думал, что это было довольно очевидно?
3. Используйте ключевое слово «partial» для реализации интерфейса в классе, так что у вас есть только реализации интерфейса в классе partial, что упрощает управление.
4. Пожалуйста. Перечитайте мой ответ, я его обновил. (Я предполагаю, что определение int для Name является типом и должно быть string ?)
5. Выбор из объекта C работает хорошо, потому что объект A рассматривает его как один ко многим. Но как получить данные от объекта B в том же запросе? Он имеет отношение «многие к одному», противоположное.