WCF服务代理未设置“FieldSpecified”属性

我有一个WCF DataContract ,如下所示:

 namespace MyCompanyName.Services.Wcf { [DataContract(Namespace = "http://mycompanyname/services/wcf")] [Serializable] public class DataContractBase { [DataMember] public DateTime EditDate { get; set; } // code omitted for brevity... } } 

当我在Visual Studio中添加对此服务的引用时,将生成此代理代码:

 ///  [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3082")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://mycompanyname/services/wcf")] public partial class DataContractBase : object, System.ComponentModel.INotifyPropertyChanged { private System.DateTime editDateField; private bool editDateFieldSpecified; ///  [System.Xml.Serialization.XmlElementAttribute(Order=0)] public System.DateTime EditDate { get { return this.editDateField; } set { this.editDateField = value; this.RaisePropertyChanged("EditDate"); } } ///  [System.Xml.Serialization.XmlIgnoreAttribute()] public bool EditDateSpecified { get { return this.editDateFieldSpecified; } set { this.editDateFieldSpecified = value; this.RaisePropertyChanged("EditDateSpecified"); } } // code omitted for brevity... } 

如您所见,除了为EditDate生成支持属性外, EditDate生成一个额外的Specified属性。 一切都很好,除了我做以下事情:

 DataContractBase myDataContract = new DataContractBase(); myDataContract.EditDate = DateTime.Now; new MyServiceClient.Update(new UpdateRequest(myDataContract)); 

EditDate没有得到服务端点的接收(不会出现在传输的XML中)。

我调试了代码并发现,虽然我设置了EditDate ,但EditDateSpecified属性并没有像我期望的那样设置为true ; 因此,XML序列化程序忽略了EditDate的值,即使它已设置为有效值。

作为一个快速的黑客,我修改了EditDate属性,如下所示:

  ///  [System.Xml.Serialization.XmlElementAttribute(Order=0)] public System.DateTime EditDate { get { return this.editDateField; } set { this.editDateField = value; // hackhackhack if (value != default(System.DateTime)) { this.EditDateSpecified = true; } // end hackhackhack this.RaisePropertyChanged("EditDate"); } } 

现在代码按预期工作,但当然每次重新生成代理时,我的修改都会丢失。 我可以将调用代码更改为以下内容:

 DataContractBase myDataContract = new DataContractBase(); myDataContract.EditDate = DateTime.Now; myDataContract.EditDateSpecified = true; new MyServiceClient.Update(new UpdateRequest(myDataContract)); 

但这似乎也是浪费时间的浪费。

最后,我的问题是:是否有人建议如何通过Visual Studio服务代理生成器的这种不直观(和IMO损坏)行为,或者我只是遗漏了什么?

它可能有点不直观(并且让我措手不及!) – 但它是处理XML模式中可能指定或可能未指定的元素的唯一正确方法。

你必须自己设置xyzSpecified标志似乎也是违反直觉的 – 但最终,这会给你更多的控制权,WCF就是关于你的意图非常明确和清晰的SOA的四个原则 。

所以基本上 – 这就是它的方式,习惯它:-)没有办法“过去”这种行为 – 这是WCF系统的设计方式,也是有充分理由的。

你总能做的就是捕捉并处理这个this.RaisePropertyChanged("EditDate"); 事件并在该事件的事件处理程序中设置EditDateSpecified标志。

试试这个

 [DataMember(IsRequired=true)] public DateTime EditDate { get; set; } 

这应该省略EditDateSpecified属性,因为该字段是根据需要指定的

您可以使用扩展类来“自动指定”(绑定更改处理程序事件),而不是更改自动生成代码的setter。 这可能有两个实现 – 一个“懒惰”( Autospecify )使用reflection来查找基于属性名称的fieldSpecified ,而不是在某些类型的开关语句(如Autonotify为每个类列出它们:

 public static class PropertySpecifiedExtensions { private const string SPECIFIED_SUFFIX = "Specified"; ///  /// Bind the  handler to automatically set any xxxSpecified fields when a property is changed. "Lazy" via reflection. ///  /// the entity to bind the autospecify event to /// optionally specify a suffix for the Specified property to set as true on changes /// optionally specify a prefix for the Specified property to set as true on changes public static void Autospecify(this INotifyPropertyChanged entity, string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null) { entity.PropertyChanged += (me, e) => { foreach (var pi in me.GetType().GetProperties().Where(o => o.Name == specifiedPrefix + e.PropertyName + specifiedSuffix)) { pi.SetValue(me, true, BindingFlags.SetField | BindingFlags.SetProperty, null, null, null); } }; } ///  /// Create a new entity and  its properties when changed ///  ///  ///  ///  ///  public static T Create(string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null) where T : INotifyPropertyChanged, new() { var ret = new T(); ret.Autospecify(specifiedSuffix, specifiedPrefix); return ret; } } 

这简化了编写便利工厂方法,例如:

 public partial class MyRandomClass { ///  /// Create a new empty instance and  its properties when changed ///  ///  public static MyRandomClass Create() { return PropertySpecifiedExtensions.Create(); } } 

一个缺点(除了reflection,meh)是你必须使用工厂方法来实例化你的类或使用.Autospecify 之前 (?)你用属性对属性进行任何更改。

没有反思

如果你不喜欢reflection,你可以定义另一个扩展类+接口:

 public static class PropertySpecifiedExtensions2 { ///  /// Bind the  handler to automatically call each class's  method on the property name. ///  /// the entity to bind the autospecify event to public static void Autonotify(this IAutoNotifyPropertyChanged entity) { entity.PropertyChanged += (me, e) => ((IAutoNotifyPropertyChanged)me).WhenPropertyChanges(e.PropertyName); } ///  /// Create a new entity and  it's properties when changed ///  ///  ///  public static T Create() where T : IAutoNotifyPropertyChanged, new() { var ret = new T(); ret.Autonotify(); return ret; } } ///  /// Used by  to standardize implementation behavior ///  public interface IAutoNotifyPropertyChanged : INotifyPropertyChanged { void WhenPropertyChanges(string propertyName); } 

然后每个类自己定义行为:

 public partial class MyRandomClass: IAutoNotifyPropertyChanged { public void WhenPropertyChanges(string propertyName) { switch (propertyName) { case "field1": this.field1Specified = true; return; // etc } } } 

当然,这样做的缺点是属性名称的魔术字符串使重构变得困难,你可以通过Expression解析来解决这个问题吗?

伊恩,请忽略我以前的答案,解释如何吮吸鸡蛋。 我投票删除了它们。

你能告诉我你正在使用哪个版本的Visual Studio吗?

在VS2005客户端 – 在生成的代码中,我得到Specified标志,但没有在更改值时引发事件。 要传递数据,我必须设置Specified标志。

在Visual Web Developer 2008 Express客户端中 – 在生成的代码中,我没有得到Specified标志,但我确实在更改值时获得了事件。

在我看来,这个function已经发展,Web Dev 2008更接近您所追求的并且更直观,因为您在设置值后不需要设置标志。

Bowthy

这是一个简单的项目,可以修改生成的WCF代码中的setter,以获取可选属性,以便在设置相关值时自动将* Specified标志设置为true。

https://github.com/b9chris/WcfClean

显然有些情况下你需要手动控制* Specified标志,所以我不是推荐给所有人,但在大多数简单的用例中,* Specified标志只是一个额外的麻烦并自动设置它们节省时间,而且通常更多直观。

请注意,如果您控制Web Service发布点, Mustafa Magdy对此处的另一个答案的评论将为您解决此问题。 但是,我通常不控制Web Service出版物而只是消耗一个,并且必须在一些简单的软件中处理* Specified标志,我希望这样自动化。 因此这个工具。

更多的信息

在这里的MSDN上

在她的回答中,Shreesha解释说:

“指定的”字段仅在结构的可选参数上生成。 (int,datetime,decimal等)。 所有这些变量都将使用名称Specified生成其他变量。

这是一种了解参数是否真正在客户端和服务器之间传递的方法。

详细说明,一个可选的整数,如果没有传递,仍然会有dafault值为0.你如何区分这个和实际传递的值为0的那个? “指定”字段可以让您知道是否传递了可选整数。 如果“指定”字段为false,则不传递该值。 如果为true,则传递整数。

基本上,设置这些字段的唯一方法是手动设置它们,如果对于已设置的字段它们未设置为true,那么该字段将在Web服务的SOAP消息中遗漏呼叫。

我最后做的是构建一个循环遍历对象的所有成员的方法,如果已经设置了属性,并且如果有一个名为name _ Specified的属性,则将其设置为true。

将代理类属性更改为可空类型

例如:

布尔? 确认

约会时间? 了checkdate