Проблемы с добавлением или удалением элементов в настраиваемый объект Result<T>

Это доводит меня до белого каления, и даже малейший толчок в правильном направлении будет очень ценен.

По сути, этот метод вызывает сторонний 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; }
}

Ваш класс Data не реализует никакого интерфейса, такого как IList, который бы обеспечивал такое поведение, и в любом случае у него нет таких методов.

insane_developer 23.12.2020 18:50

Как выглядит json из службы, которую вы вызываете? Как вы хотите, чтобы json из вашего сервиса выглядел?

Caius Jard 23.12.2020 19:23

пс; Одним из преимуществ наличия атрибутов jsonProperty является то, что вы можете отказаться от этих действительно мягких/бессмысленных имен для своих свойств и называть их так, чтобы это помогло вам понять код, но пусть вывод json будет соответственно бессмысленным (пинайте банку в будущем бедному разработчику, который должен его потреблять) - если вы также не определяете json, и в этом случае я рекомендую лучшие имена

Caius Jard 23.12.2020 19:25

Также; относится ли тег efcore к этому вопросу?

Caius Jard 23.12.2020 19:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для решения проблемы, над которой вы работаете, вам будет полезно узнать о 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 год будет отличным :)

theConfusedCoder 19.01.2021 15:51

Другие вопросы по теме