尝试创建通用方法以避免代码重复
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);
我正在开发一个 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);