Попытка создать общий метод c, чтобы избежать повторения кода - PullRequest
1 голос
/ 05 августа 2020

Я работаю над приложением C#, и у меня есть 2 (скоро будет 3, 4 и более) метода, которые имеют такую ​​похожую структуру, что их просят преобразовать в нечто более общее c. Вот 2 образца, вы увидите сходство.

Метод 1:

 public async Task<APIGatewayProxyResponse> McaEventStoreRecvdPointsCouponProxyResponse(APIGatewayProxyRequest request, ILambdaContext context)
    {
        try
        {
            string thisRequestId = Guid.NewGuid().ToString();
            if (request.PathParameters.Any())
            {
                var cardNumber = request.PathParameters.FirstOrDefault(x => x.Key.ToLower() == "card_number").Value;
                context.Logger.LogLine($"MCA Event store event [{cardNumber}]");

                var restValueVoucher = JsonConvert.DeserializeObject<RootObjectRestValueVoucherPayload>(request.Body);
                RestValueVoucherPayloadValidator validator = new RestValueVoucherPayloadValidator();
                ValidationResult results = validator.Validate(restValueVoucher.Payload);

                if (!results.IsValid) throw new SchemaValidationException(results.Errors);

                var dbRestValueVoucher = restValueVoucher.Payload.Convert(restValueVoucher.Payload);

                dbRestValueVoucher.CardNumber = cardNumber;
                loyaltyContext.Add(dbRestValueVoucher);
                int rowsAffected = await loyaltyContext.SaveChangesAsync();
                context.Logger.LogLine($"Database changes applied {rowsAffected}");

                return GenerateResponse(HttpStatusCode.OK, new EventStoreResponse(context,
                    RequestResponseTypes.EVENT_STORE, thisRequestId,
                    restValueVoucher.Payload));
            }
            else
            {
                return GenerateResponse(HttpStatusCode.OK, new TestResponse(context, RequestResponseTypes.TEST_REQUEST));
            }
        }
        catch (SchemaValidationException schemaEx)
        {
            context.Logger.LogLine(schemaEx.Message);
            return GenerateResponse(HttpStatusCode.BadRequest, schemaEx);
        }
        catch (Exception ex)
        {
            context.Logger.LogLine($"{ex}");

            LcsException lcsException = new LcsException(ex);
            return GenerateResponse(HttpStatusCode.BadRequest,
                lcsException);
        }
    }

Метод 2:

    public async Task<APIGatewayProxyResponse> McaEventStoreTierChangeProxyResponse(APIGatewayProxyRequest request, ILambdaContext context)
    {
        try
        {
            string thisRequestId = Guid.NewGuid().ToString();
            if (request.PathParameters.Any())
            {
                var cardNumber = request.PathParameters.FirstOrDefault(x => x.Key.ToLower() == "card_number").Value;
                context.Logger.LogLine($"MCA Event store event [{cardNumber}]");

                var tierChange = JsonConvert.DeserializeObject<RootObjectTierChangePayload>(request.Body);
                TierChangePayloadValidator validator = new TierChangePayloadValidator();
                ValidationResult results = validator.Validate(tierChange.Payload);

                if (!results.IsValid) throw new SchemaValidationException(results.Errors);

                var dbTierChange = tierChange.Payload.Convert(tierChange.Payload);
                dbTierChange.CardNumber = cardNumber;
                loyaltyContext.Add(dbTierChange);
                int rowsAffected = await loyaltyContext.SaveChangesAsync();
                context.Logger.LogLine($"Database changes applied {rowsAffected}");

                return GenerateResponse(HttpStatusCode.OK, new EventStoreResponse(context,
                    RequestResponseTypes.EVENT_STORE, thisRequestId,
                    tierChange.Payload));
            }
            else
            {
                return GenerateResponse(HttpStatusCode.OK, new TestResponse(context, RequestResponseTypes.TEST_REQUEST));
            }
        }
        catch (SchemaValidationException schemaEx)
        {
            context.Logger.LogLine(schemaEx.Message);
            return GenerateResponse(HttpStatusCode.BadRequest, schemaEx);
        }
        catch (Exception ex)
        {
            context.Logger.LogLine($"{ex}");

            LcsException lcsException = new LcsException(ex);
            return GenerateResponse(HttpStatusCode.BadRequest,
                lcsException);
        }
    }

Я начал работать над обобщенным методом c и дошел до этого:

    private static TPayload ProcessTest<TPayload, TEvent>(TPayload payload, TEvent myevent, string body, AbstractValidator<TPayload> validator)
        where TPayload : Payload
        where TEvent : IEventStore
    {
        var test = JsonConvert.DeserializeObject<TPayload>(body);
        ValidationResult results = validator.Validate(?)
    }

Моя проблема связана с рефакторингом этой строки на данный момент: ValidationResult results = validator.Validate (tierChange.Payload ). tierChange - это JSON 'Root объект', который позволяет мне принимать входящие JSON в следующем формате:

{
  "Message": {
    "message-id": 1000,
    "old-tier": "SISTERCLUB",
    "new-tier": "DIAMOND",
    "timestamp-of-change": "2020-07-27T00:00:00",
    "anniversary-date": "2020-07-28T00:00:00"
  }
}

Структура очень похожа на входящую JSON для метода 1, который равен:

{
  "Message": {
      "message-id": 10000,
      "redeemed-voucher-instance-id":123,
      "new-voucher-instance-id":1234,
      "initial-voucher-value": 5.00,
      "rest-voucher-value":15.00,
      "valid-from": "2020-07-27T00:00:00",
      "valid-to": "2021-07-27T00:00:00",
      "description": "$5 BIRTHDAY VOUCHER",
      "unit": "AUD"
  }
}

.Payload используется для доступа к содержимому внутри объекта root в обоих случаях (содержимое, уникальное для каждого объекта). Вот пример объекта Tier Change Root и Payload (кроме различных свойств внутри Payload, другой объект такой же).

Объект root:

public class RootObjectTierChangePayload 
{
    [JsonProperty(PropertyName = "Message")]
    public TierChangePayload Payload { get; set; }
}

И внутренний объект:

 public partial class TierChangePayload : Payload, ITransform<TierChangePayload, TierChange>, IEventStore 
    {
        [JsonProperty(PropertyName = "message-id")]
        public int MessageId { get; set; }

        /// <summary>
        /// </summary>
        [JsonProperty(PropertyName = "old-tier")]
        public string OldTier { get; set; }

        /// <summary>
        /// </summary>
        [JsonProperty(PropertyName = "new-tier")]
        public string NewTier { get; set; }

        /// <summary>
        /// </summary>
        [JsonProperty(PropertyName = "timestamp-of-change")]
        public DateTime TimestampOfChange { get; set; }

        /// <summary>
        /// </summary>
        [JsonProperty(PropertyName = "anniversary-date")]
        public DateTime AnniversaryDate { get; set; }

        public TierChange Convert(TierChangePayload source)
        {
            TierChange tierChange = new TierChange
            {
                CreatedTimestamp = Functions.GenerateDateTimeByLocale(),
                ChangeTimestamp = null,
                AnniversaryDate = this.AnniversaryDate,
                MessageId = this.MessageId,
                NewTierId = this.NewTier,
                OldTierId = this.OldTier
            };

            return tierChange; 
        }

        public string ToJson()
        {
            throw new NotImplementedException();
        }
    }

Как мне настроить объекты, которые я использую, чтобы я мог лучше обобщить их для соответствия общему методу c? На данный момент я не могу получить доступ к .Payload в общем методе c.

Ответы [ 2 ]

1 голос
/ 05 августа 2020

Обновление В C# вы можете передавать блоки кода (делегаты) другим блокам кода как тип Action<T> или тип Func<T> (с переменным числом общих c аргументов). Эти типы просто инкапсулируют ваш код и полезны в таких случаях, как ваш, где метод почти такой же, за исключением пары строк. Вы можете взять эту пару строк и передать их в качестве параметра методу.

Action<> - это блок кода, который принимает аргументы T и возвращает void. Func<> - это кодовый блок, который принимает 0 или несколько аргументов T1 и возвращает результат T.

Обратите внимание, что при компиляции эти кодовые блоки превращаются в c методы stati и, таким образом, являются чисто синтаксическими c сахар.

Конец обновления

Итак, ваш метод generi c может выглядеть так:

public async Task<APIGatewayProxyResponse> GenericMethod<T>(APIGatewayProxyRequest request, ILambdaContext context, Func<string, (T, ValidationResult, string)> validationFunc) where T: class
{
    try
    {
        string thisRequestId = Guid.NewGuid().ToString();
        if (request.PathParameters.Any())
        {
            var cardNumber = request.PathParameters.FirstOrDefault(x => x.Key.ToLower() == "card_number").Value;
            context.Logger.LogLine($"MCA Event store event [{cardNumber}]");

            var validationAndData = validationFunc(request.Body);
            ValidationResult results = validationAndData.Item2;

            if (!results.IsValid) throw new SchemaValidationException(results.Errors);

            loyaltyContext.Add(validationAndData.Item1);
            int rowsAffected = await loyaltyContext.SaveChangesAsync();
            context.Logger.LogLine($"Database changes applied {rowsAffected}");

            return GenerateResponse(HttpStatusCode.OK, new EventStoreResponse(context,
                RequestResponseTypes.EVENT_STORE, thisRequestId,
                validationAndData.Item3));
        }
        else
        {
            return GenerateResponse(HttpStatusCode.OK,
             new TestResponse(context, RequestResponseTypes.TEST_REQUEST));
        }
    }
    catch (SchemaValidationException schemaEx)
    {
        context.Logger.LogLine(schemaEx.Message);
        return GenerateResponse(HttpStatusCode.BadRequest, schemaEx);
    }
    catch (Exception ex)
    {
        context.Logger.LogLine($"{ex}");

        LcsException lcsException = new LcsException(ex);
        return GenerateResponse(HttpStatusCode.BadRequest,
            lcsException);
    }
}

Затем вы можете преобразовать другой два вот так:

public async Task<APIGatewayProxyResponse> McaEventStoreRecvdPointsCouponProxyResponse(APIGatewayProxyRequest request, ILambdaContext context)
{
    return await GenericMethod(request, context, (body) => {
        var restValueVoucher = JsonConvert.DeserializeObject<RootObjectRestValueVoucherPayload>(request.Body);
        RestValueVoucherPayloadValidator validator = new RestValueVoucherPayloadValidator();
        var dbRestValueVoucher = restValueVoucher.Payload.Convert(restValueVoucher.Payload);
        dbRestValueVoucher.CardNumber = cardNumber;

        return (dbRestValueVoucher, validator.Validate(restValueVoucher.Payload), restValueVoucher.Payload);
    });
}

public async Task<APIGatewayProxyResponse> McaEventStoreTierChangeProxyResponse(APIGatewayProxyRequest request, ILambdaContext context)
{
    return await  GenericMethod(request, context, (body) => {
        var tierChange = JsonConvert.DeserializeObject<RootObjectTierChangePayload>(request.Body);
            TierChangePayloadValidator validator = new TierChangePayloadValidator();
            var dbTierChange = tierChange.Payload.Convert(tierChange.Payload);
            dbTierChange.CardNumber = cardNumber;
            return (dbTierChange, validator.Validate(tierChange.Payload), tierChange.Payload);
    });
}
0 голосов
/ 05 августа 2020

Если вы создадите generi c root объект

public class RootObject<T> 
{
    [JsonProperty(PropertyName = "Message")]
    public T Payload { get; set; }
}

Это может сработать, если json сможет справиться с этим.

var tierChange = JsonConvert.DeserializeObject<RootObject<TPayload>>(request.Body);
ValidationResult results = validator.Validate(tierChange.Payload);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...