#entity-framework #asp.net-web-api #configuration #foreign-keys #lazy-loading
#entity-framework #asp.net-web-api #конфигурация #внешние ключи #отложенная загрузка
Вопрос:
У меня есть две модели для двух таблиц, имеющих отношение к внешнему ключу. Я должен получить запись из обеих таблиц в одном запросе.
Структура модели выглядит следующим образом:-
- EmployeeRecord.cs
public partial class EmployeeRecord
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public tblEmployeeRecord()
{
this.countries = new HashSet<tblCountry>();
}
public int EmployeeId { get; set; }
public string Name { get; set; }
public string userName { get; set; }
public string userRole { get; set; }
public string id { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Country> countries { get; set; }
}
2.Country.cs
public partial class Country
{
public int CountryId { get; set; }
public int EmployeeId { get; set; }
public string CountryName { get; set; }
public virtual EmployeeRecord employeeRecord { get; set; }
}
- EmployeeController.cs
public class EmployeeRecordsController : ApiController
{
private EstorageEntitiesforCombineView db = new EstorageEntitiesforCombineView();
// GET: api/EmployeeRecords
public IEnumerable<EmployeeRecord> GetEmployeeRecords([FromUri]PagingParameterModel pagingparametermodel)
{
var source= db.EmployeeRecords.OrderBy(a => a.EmployeeId);
// Get's No of Rows Count
if (pagingparametermodel.userRole == "HR")
{
source= (from a in db.EmployeeRecords
where a.userRole == "HR"
select a).OrderBy(a => a.EmployeeId);
}
else if(pagingparametermodel.userRole == "eStorage Admin")
{
source = (from a in db.EmployeeRecords
where a.userRole == "eStorage Admin"
select a).OrderBy(a => a.EmployeeId);
} else if(pagingparametermodel.userRole == "SharedService")
{
source = (from a in db.EmployeeRecords
where a.userRole == "SharedService"
select a).OrderBy(a => a.EmployeeId);
}
if(pagingparametermodel.toSearch == 1 amp;amp; pagingparametermodel.userRole == "ALL")
{
source = (from b in db.EmployeeRecords
where b.Name.StartsWith(pagingparametermodel.name)
select b).OrderBy(a => a.EmployeeId);
}else if (pagingparametermodel.toSearch==1) {
source= (from b in db.EmployeeRecords
where b.Name.StartsWith(pagingparametermodel.name) amp;amp; b.userRole==pagingparametermodel.userRole
select b).OrderBy(a => a.EmployeeId);
}
int count = source.Count();
// Parameter is passed from Query string if it is null then it default Value will be pageNumber:1
int CurrentPage = pagingparametermodel.pageNumber;
// Parameter is passed from Query string if it is null then it default Value will be pageSize:20
int PageSize = pagingparametermodel.pageSize;
// Display TotalCount to Records to User
int TotalCount = count;
// Calculating Totalpage by Dividing (No of Records / Pagesize)
int TotalPages = (int)Math.Ceiling(count / (double)PageSize);
// Returns List of Customer after applying Paging
var items = source.Skip((CurrentPage - 1) * PageSize).Take(PageSize).ToList();
// if CurrentPage is greater than 1 means it has previousPage
var previousPage = CurrentPage > 1 ? "Yes" : "No";
// if TotalPages is greater than CurrentPage means it has nextPage
var nextPage = CurrentPage < TotalPages ? "Yes" : "No";
// Object which we are going to send in header
var paginationMetadata = new
{
totalCount = TotalCount,
pageSize = PageSize,
currentPage = CurrentPage,
totalPages = TotalPages,
previousPage,
nextPage
};
// Setting Header
System.Web.HttpContext.Current.Response.Headers.Add("Paging-Headers", Newtonsoft.Json.JsonConvert.SerializeObject(paginationMetadata));
// Returing List of Customers Collections
return items;
}
}
Я хочу получить следующий ответ :-
[{
"tblCountries": [
{
"CountryId": 265,
"EmployeeId": 350,
"CountryName": "INWEST"
}
],
"EmployeeId": 350,
"Name": "ABC",
"userName": "abc@mail.com",
"userRole": "HR",
"id": nbh546652n45
}]
Но ответ следующий:-
[{
"tblCountries": [],
"EmployeeId": 350,
"Name": "ABC",
"userName": "abc@mail.com",
"userRole": "HR",
"id": nbh546652n45
}]
Я также пробовал, Include(a => a.countries)
следующим образом:-
source= (from a in db.EmployeeRecords
where a.userRole == "HR"
select a).Include(a => a.countries).OrderBy(a => a.EmployeeId);
Но я получаю следующую ошибку:-
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Object graph for type 'System.Collections.Generic.HashSet`1[[eStorageApi.Models.tblCountry, eStorageApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' contains cycles and cannot be serialized if reference tracking is disabled.
</ExceptionMessage>
<ExceptionType>
System.Runtime.Serialization.SerializationException
</ExceptionType>
Пытался включить эти LOCS в WebApiConfig.cs:-
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;
Все еще получаю ту же ошибку.
Комментарии:
1. Сущности представляют данные. То, что возвращает ваш API, должно быть определено и выбрано из объекта, а не пытаться передать объект напрямую. (да, многие примеры там делают это, все еще очень плохая / ленивая практика) Определите структуру и поля, которые вы хотите вернуть как класс POCO DTO, и используйте
.Select()
или AutoMapper.ProjectTo()
для его заполнения. Не рекомендуется предоставлять клиенту доступ к базе данных, даже для выполнения ВЫБОРА * ИЗ WHERE для таблицы, что и означает возврат объектов.
Ответ №1:
Спроецируйте свою модель домена на что-то другое, прежде чем возвращать ее из вашего api. Это лучше контролирует формат данных ответа, и если добавляется свойство (например, ssn), оно не раскрывается случайно. Это также имеет дополнительное преимущество в исправлении ваших циклических ссылок.
return items.Select(i => new {
i.Name,
i.Username,
...
Countries = i.Countries.Select(c => new {
c.CountryId,
c.CountryName,
...
}
};
Ответ №2:
Вам нужно установить связь между employeeRecord и странами. Попробуйте с этим:
source= db.EmployeeRecords.Include(e => e.countries).Select(c => c.employeeRecord)
.Where(a => a.userRole == "HR");
И когда вы выполняете запрос, вам нужно указать текст: Application / Json
Комментарии:
1. Привет @mlvictor, который я использую, как показано ниже, дает тот же результат :
2. var source = db.tblEmployeeRecords. Включить (e => e.tblCountries). OrderBy(a => a.EmployeeID);