尝试创建通用方法以避免代码重复

Trying to create a generic method to avoid repetition of code

我正在开发一个 C# 应用程序,我有 2 个(很快会是 3 个、4 个或更多)方法,它们具有如此相似的结构,它们恳求转换为更通用的东西。这里有2个样本,你会看到相似之处。

方法一:

 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);
        }
    }

方法二:

    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);
        }
    }

我开始研究泛型方法,并走到了这一步:

    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 object',它允许我接受以下格式的传入 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非常相似,即:

{
  "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": " BIRTHDAY VOUCHER",
      "unit": "AUD"
  }
}

在这两种情况下,.Payload 都用于访问根对象内的内容(每个对象唯一的内容)。下面是Tier Change Root对象和Payload的例子(除了Payload内部的属性不同,其他对象是一样的)。

根对象:

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();
        }
    }

如何调整我正在使用的对象,以便更好地概括它们以适应泛型方法?目前,我无法在通用方法中访问 .Payload

更新 在 C# 中,您可以将代码块(委托)作为 Action<T> 类型或 Func<T> 类型(具有可变数量的通用参数)传递给其他代码块。 这些类型只是封装了您的代码,并且在像您这样的情况下很有用 - 除了几行之外,方法几乎相同。您可以采用这几行并将它们作为参数传递给该方法。

Action<> 是一个带有 T 个参数和 returns void 的代码块。 Func<> 是一个带有 0 个或多个 T1 参数和 returns T 结果的代码块。

请注意,在编译时,这些代码块会变成静态方法,因此纯粹是一种语法糖。

结束更新

因此您的通用方法可以如下所示:

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);
    });
}

如果创建通用根对象

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);