前面一篇文章,我们介绍了使用Enterprise Libary VAB来实现WCF参数验证。你有可能感觉Enterpise Library 太重量级了,是的,现在我们还要可以借助Data Annotations来实现轻量级解决方案,它曾用于Asp.net MVC等应用程序中,您可能比较熟悉。它们是集成于.net framework。这里我们关键是要实现IParameterInspector接口, 下面是一部分的核心代码:
public class ValidatingParameterInspector: IParameterInspector
{
private readonly IEnumerable<IObjectValidator> _validators;
private readonly IErrorMessageGenerator _errorMessageGenerator;
public ValidatingParameterInspector(IEnumerable<IObjectValidator> validators, IErrorMessageGenerator errorMessageGenerator)
{
ValidateArguments(validators, errorMessageGenerator);
_validators = validators;
_errorMessageGenerator = errorMessageGenerator;
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
}
public object BeforeCall(string operationName, object[] inputs)
{
var validationResults = new List<ValidationResult>();
foreach (var input in inputs)
{
foreach (var validator in _validators)
{
var results = validator.Validate(input);
validationResults.AddRange(results);
}
}
if (validationResults.Count > 0)
{
throw new FaultException(_errorMessageGenerator.GenerateErrorMessage(operationName, validationResults));
}
return null;
}
private static void ValidateArguments(IEnumerable<IObjectValidator> validators, IErrorMessageGenerator errorMessageGenerator)
{
if (validators == null)
{
throw new ArgumentNullException("validators");
}
if (!validators.Any())
{
throw new ArgumentException("At least one validator is required.");
}
if (errorMessageGenerator == null)
{
throw new ArgumentNullException("errorMessageGenerator");
}
}
}
在CodePlex上有一个WCF Data Annotations开源项目就是解决这个问题。下面我们来看如何使用:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
Service对应的DataContract,您可以看到我们标记[Required]和[StringLength]验证。
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = string.Empty;
[DataMember]
[Required]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
[Required]
[StringLength(500, MinimumLength = 5)]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
实现在Service的代码如下:
//[ValidateDataAnnotationsBehavior]
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
如果您使用配置文件就不需要标注ValidateDataAnnotationsBehaviorAttribute了:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="validateDataAnnotationsBehavior"
type="DevTrends.WCFDataAnnotations.ValidateDataAnnotationsBehaviorExtensionElement, DevTrends.WCFDataAnnotations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior>
<validateDataAnnotationsBehavior/>
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
我们来写一些基于xunit的单元测试 来验证它们:
/// <summary>
/// Wcf unit testing for http://localhost:17347/WcfService.svc
/// </summary>
public class WcfTestingWCFDataAnnotations
{
[Fact]
public void Should_Arise_Validation_Exception_With_ShortString()
{
var client = new Service1Client();
var composetype = new CompositeType
{
BoolValue = true,
StringValue = "two"
};
var exception = Assert.Throws<FaultException>(
() => { var result = client.GetDataUsingDataContract(composetype); }
);
Assert.Equal(
@"Service operation GetDataUsingDataContract failed due to validation errors:
StringValue: The field must be a string with a minimum length of 5 and a maximum length of 500.
",
exception.Message);
}
[Fact]
public void Should_Get_CorrectString()
{
var client = new Service1Client();
var composetype = new CompositeType
{
BoolValue = true,
StringValue = "twothree"
};
var result=client.GetDataUsingDataContract(composetype);
composetype.StringValue += "Suffix";
Assert.Equal(composetype.StringValue, result.StringValue);
}
}
您还可以使DataContract实现IValidatableObject接口 达到验证的效果:
[DataContract]
public class CompositeType : IValidatableObject
{
bool boolValue = true;
string stringValue = string.Empty;
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (StringValue.Length < 5)
{
yield return new ValidationResult("StringValue must be greater than 5", new[] { "StringValue" });
}
}
}
这是一个简单示例,希望对您WCF开发有帮助。
您可能感兴趣的文章:
使用EnterpriseLibrary Validation Block对WCF做验证
构建WCF的消息代理
作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
该文章也同时发布在我的独立博客中-Petter Liu Blog。