集成Data Annotations到WCF实现验证

       前面一篇文章,我们介绍了使用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

你可能感兴趣的:(Annotations)