EntLib WCF集成传ref参数时抛出类型参数异常问题咨询
首先可以明确:这是Enterprise Library Validation集成WCF时的一个已知局限,不是你的操作问题。
问题原因
当你给ref类型的参数(比如ref RoutingCodes routingCode)添加验证属性时,EL的WCF验证拦截器(ValidationParameterInspector)在创建验证器时,错误地把参数的**引用类型(即RoutingCodes&,带地址符号的类型)**作为泛型参数传入CreateValidator<T>这类方法。而.NET的泛型系统根本不允许将引用类型(T&)作为泛型类型参数,这就直接抛出了你看到的ArgumentException。
可行的规避方案
这里有几个不用放弃EL验证块的解决思路:
1. 手动在方法内部验证ref参数
不在契约的ref参数上直接加验证属性,而是在服务方法的实现里,手动创建EL验证器来验证routingCode:
public Hubs GetRoute(string sourceDepotId, string countryCode, string zipCode, DateTime shippingDate, DateTime? deliveryDate, ref RoutingCodes routingCode, out string errorMessage) { // 手动创建RoutingCodes的验证器 var validator = ValidationFactory.CreateValidator<RoutingCodes>(); var validationResults = validator.Validate(routingCode); if (!validationResults.IsValid) { // 拼接错误信息到errorMessage errorMessage = string.Join("; ", validationResults.Select(r => r.Message)); // 返回合适的默认值或处理逻辑 return null; } // 正常业务逻辑... errorMessage = string.Empty; return new Hubs(); }
这种方式最直接,不需要修改契约结构。
2. 将ref参数包装成DTO类
把ref的枚举参数放到一个自定义的DTO类中,然后对DTO的属性添加验证属性,再把DTO作为参数传入(不用ref):
// 定义DTO类 public class RoutingCodeContainer { [NotNullValidator(MessageTemplate = "路由代码不能为空")] // 如果需要其他验证,也可以加在这里 public RoutingCodes RoutingCode { get; set; } } // 修改操作契约 [OperationContract] Hubs GetRoute(string sourceDepotId, [NotNullValidator(MessageTemplate = "...")] [RegexValidator("[a-zA-Z]", MessageTemplate = "...")] [StringLengthValidator(2, RangeBoundaryType.Inclusive, 2, RangeBoundaryType.Inclusive, MessageTemplate = "...")] string countryCode, string zipCode, DateTime shippingDate, DateTime? deliveryDate, RoutingCodeContainer routingCodeContainer, out string errorMessage);
在服务实现里,你可以直接修改routingCodeContainer.RoutingCode的值,达到和ref参数一样的输出效果,同时EL可以正常验证DTO的属性。
3. 自定义WCF验证行为(进阶)
如果你想保留ref参数和契约上的验证属性,可以自定义ValidationParameterInspector,重写它处理参数类型的逻辑:
- 继承原有的
ValidationParameterInspector - 在
CreateInputParameterValidator方法中,判断参数是否是ref/out类型(通过ParameterInfo.ParameterType.IsByRef) - 如果是,获取参数的实际类型(
param.ParameterType.GetElementType()),用这个类型来创建验证器,而不是直接用param.ParameterType
示例代码大概是这样:
public class CustomValidationParameterInspector : ValidationParameterInspector { public CustomValidationParameterInspector(OperationDescription operation, string ruleSet) : base(operation, ruleSet) { } protected override Validator CreateInputParameterValidator(ParameterInfo param, string ruleSet) { // 处理ref/out参数,获取实际类型 Type targetType = param.ParameterType.IsByRef ? param.ParameterType.GetElementType() : param.ParameterType; return ValidationFactory.CreateValidator(targetType, ruleSet); } }
然后替换EL默认的ValidationBehavior,使用你自定义的Inspector。这种方式需要对EL的WCF集成做少量扩展,但能完全保留原有契约结构。
内容的提问来源于stack exchange,提问作者René Schindhelm

