Это доводит меня до белого каления, и даже малейший толчок в правильном направлении будет очень ценен.
По сути, этот метод вызывает сторонний API для получения списка бронирований для данного пользователя, который я хочу затем сравнить с таблицей ссылок идентификаторов бронирований и номеров участников, чтобы они соответствовали loggedInMemberNumber, который передается в метод, и возвращаются только бронирования, сделанные авторизованным пользователем.
Моя проблема в том, что я потерялся в том, как использовать первый ответ - идентификаторы бронирования, которые я хочу создать, хранятся в response.Response.Data.Id, но объекты Result и Dto моделируются запутанно (на мой, по общему признанию, неопытный взгляд !) и я не могу понять, что я должен добавить к модели, чтобы я мог правильно перемещаться по ней.
Intelisense может видеть response.Response.Data и response.Response.Included, но нет способа добавить или удалить элементы. Должен ли я добавить собственный класс в модель, чтобы добиться этого? Как?
Получить:
public async Task<IActionResult> GetBookingForUser(string policyNumber, string memberNumber, string loggedInMemberNumber)
{
if (!string.IsNullOrEmpty(policyNumber) && !string.IsNullOrEmpty(memberNumber) && !string.IsNullOrEmpty(loggedInMemberNumber))
{
var response = new Result<BookingListDto>();
var getUserResponse = await _medicalSolutionsRepository.GetMedicalSolutionsUserIdOrRegisterUser(memberNumber, policyNumber, false);
if (getUserResponse.Success)
{
response = await _medicalSolutionsRepository.GetBookingsForUser(getUserResponse.Response.UserId.ToString());
if (!response.Success)
{
return BadRequest(response);
}
var loggedInMemberBookings = _apiDbContext.MedicalSolutionsBookings;
List<long> bookingIdsByLoggedInUser = new List<long>();
foreach (var booking in loggedInMemberBookings)
{
if (booking.MemberNumber == loggedInMemberNumber)
{
bookingIdsByLoggedInUser.Add(booking.BookingId);
}
}
//This is where I want to create an object to store the correct bookings from the original response
Result<BookingListDto> correctBookings = new Result<BookingListDto>();
foreach (var responseBookingData in response.Response.Data)
{
foreach (var bookingId in bookingIdsByLoggedInUser)
{
if (responseBookingData.Id == bookingId)
{
//This is where I fall apart, Data has no method to add or remove items
correctBookings.Response.Data.Add(responseBookingData);
}
}
}
// Included needs to be filled with these values also
foreach (var responseBookingIncluded in response.Response.Included)
{
foreach (var bookingId in bookingIdsByLoggedInUser)
{
if (responseBookingIncluded.Id == bookingId)
{
correctBookings.Response.Data.Add(responseBookingIncluded);
}
}
}
response.Response.Data = correctBookings.Response.Data;
response.Response.Included = correctBookings.Response.Included;
}
return ApiOk(response);
}
return ApiBadRequest("UserId is a required field");
}
Модель результата:
public class Result
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("errors")]
public IEnumerable<Error> Errors { get; set; }
[JsonProperty("first_error")]
public Error FirstError => Errors?.FirstOrDefault();
[JsonProperty("status_code")]
public int StatusCode { get; set; }
public Result()
{ }
protected Result(bool success, HttpStatusCode statusCode, string message = null) : this(success, statusCode, message, Enumerable.Empty<Error>()) { }
protected Result(bool success, HttpStatusCode statusCode, string message, IEnumerable<Error> errors)
{
Success = success;
StatusCode = (int)statusCode;
Message = message;
Errors = errors ?? Enumerable.Empty<Error>();
}
public static Result Ok(HttpStatusCode statusCode) => new Result(true, statusCode);
public static Result Ok(HttpStatusCode statusCode, string message) => new Result(true, statusCode, message);
public static Result Fail(HttpStatusCode statusCode) => new Result(false, statusCode);
public static Result Fail(HttpStatusCode statusCode, string message) => new Result(false, statusCode, message);
public static Result Fail(HttpStatusCode statusCode, string message, IEnumerable<Error> errors) => new Result(false, statusCode, message, errors);
}
public class Result<T> : Result
{
public T Response { get; set; }
//[JsonConstructor]
public Result()
{
}
protected Result(bool success, HttpStatusCode statusCode, T data) : this(success, statusCode, data, null) { }
protected Result(bool success, HttpStatusCode statusCode, T data, string message) : this(success, statusCode, data, message, Enumerable.Empty<Error>()) { }
protected Result(bool success, HttpStatusCode statusCode, T data, string message, IEnumerable<Error> errors) : base(success, statusCode, message, errors)
{
Response = data;
}
public static Result<T> Ok(T data, HttpStatusCode statusCode)
{
return new Result<T>(true, statusCode, data);
}
public static Result<T> Ok(T data, HttpStatusCode statusCode, string message)
{
return new Result<T>(true, statusCode, data, message);
}
public new static Result<T> Fail(HttpStatusCode statusCode)
{
return new Result<T>(false, statusCode, default(T));
}
public static Result<T> Fail(T data, HttpStatusCode statusCode, string message)
{
return new Result<T>(false, statusCode, data, message);
}
public static Result<T> Fail(string message, HttpStatusCode statusCode)
{
return new Result<T>(false, statusCode, default(T), message);
}
public static Result<T> Fail(string message, HttpStatusCode statusCode, IEnumerable<Error> errors)
{
return new Result<T>(false, statusCode, default(T), message, errors);
}
}
Модель ДТО:
public partial class BookingListDto
{
[JsonProperty("data")]
public IEnumerable<Data> Data { get; set; }
[JsonProperty("included")]
public IEnumerable<Included> Included { get; set; }
}
public partial class Included
{
[JsonProperty("type")]
public string Type { get; set; } = "appointments";
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("attributes")]
public IncludedAttributes Attributes { get; set; }
}
public partial class Data
{
[JsonProperty("type")]
public string Type { get; set; } = "bookings";
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("attributes")]
public Attributes Attributes { get; set; }
[JsonProperty("relationships")]
public Relationships Relationships { get; set; }
}
Как выглядит json из службы, которую вы вызываете? Как вы хотите, чтобы json из вашего сервиса выглядел?
пс; Одним из преимуществ наличия атрибутов jsonProperty является то, что вы можете отказаться от этих действительно мягких/бессмысленных имен для своих свойств и называть их так, чтобы это помогло вам понять код, но пусть вывод json будет соответственно бессмысленным (пинайте банку в будущем бедному разработчику, который должен его потреблять) - если вы также не определяете json, и в этом случае я рекомендую лучшие имена
Также; относится ли тег efcore к этому вопросу?
Для решения проблемы, над которой вы работаете, вам будет полезно узнать о LINQ. Примеры, которые я приведу ниже, используют версию этой технологии «метода расширения».
Предполагая, что _apiDbContext
является чем-то вроде контекста базы данных Entity Framework, вы никогда не должны делать что-то вроде этого:
var loggedInMemberBookings = _apiDbContext.MedicalSolutionsBookings;
List<long> bookingIdsByLoggedInUser = new List<long>();
foreach (var booking in loggedInMemberBookings)
{
if (booking.MemberNumber == loggedInMemberNumber)
{
bookingIdsByLoggedInUser.Add(booking.BookingId);
}
}
Это заставит код пройти через каждую строку в таблице. Правильный способ сделать это — использовать запрос LINQ. Например:
var bookingIdsByLoggedInUser = _apiDbContext.MedicalSolutionsBookings
.Where(row => row.MemberNumber == loggedInMemberNumber)
.ToList();
В этой версии используется оператор LINQ Where , который будет преобразован в SQL, включая правильное предложение WHERE
с помощью Entity Framework, и будет обрабатываться базой данных. Это может значительно повысить производительность, особенно для больших таблиц. При этом также используется оператор LINQ ToList для «материализации» запроса в список элементов в памяти.
Что касается той части, где вы разваливаетесь, есть несколько проблем. Во-первых, Data
свойство BookingListDto
является IEnumerable
, у которого нет Add
метода. Во-вторых, класс Data
, используемый BookingListDto
, не совпадает с классом, возвращаемым сторонним API, поэтому вам нужно будет скопировать данные из стороннего ответа в свой класс Data
. Наконец, ваш Result<>
класс предлагает несколько фабричных методов, которые будут более чистым способом создания результата.
Я думаю, вам лучше будет подумать о создании списка элементов, которые вы хотите вернуть, создать из него DTO, а затем использовать фабричный метод для создания результата из этого. Итак, вместо:
//This is where I want to create an object to store the correct bookings from the original response
Result<BookingListDto> correctBookings = new Result<BookingListDto>();
foreach (var responseBookingData in response.Response.Data)
{
foreach (var bookingId in bookingIdsByLoggedInUser)
{
if (responseBookingData.Id == bookingId)
{
//This is where I fall apart, Data has no method to add or remove items
correctBookings.Response.Data.Add(responseBookingData);
}
}
}
Вы можете снова использовать LINQ, чтобы создать список элементов ответа, а затем вызвать фабричный метод для создания результата, например:
// first create the list of bookings to return
var bookings = response.Response.Data
.Where(responseBookingData => bookingIdsByLoggedInUser.Any(id => id == responseBookingData.Id)
.Select(responseBookingData => new Data { /* copy properties from resonseBookingData here */ })
.ToList();
var dto = new BookingListDto { Data = bookings };
var response = Result<BookingListDto>.Ok(dto, HttpStatusCode.OK);
В дополнение к операторам Where
и ToList
здесь используются операторы LINQ Any и Select.
Обратите внимание, что оператор ToList
возвращает List<>
, который реализует интерфейс IEnumerable<>
и, следовательно, присваивается свойству BookingListDto.Data
.
Я хотел вернуться к вам в то время, но вмешались праздники, и я вернулся к этой проблеме только после этого - ваш пост очень помог, и спасибо за ваш подробный ответ. Я надеюсь, что ваш 2021 год будет отличным :)
Ваш класс Data не реализует никакого интерфейса, такого как
IList
, который бы обеспечивал такое поведение, и в любом случае у него нет таких методов.