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客户端 – 在生成的代码中,我得到
标志,但没有在更改值时引发事件。 要传递数据,我必须设置
标志。
在Visual Web Developer 2008 Express客户端中 – 在生成的代码中,我没有得到
标志,但我确实在更改值时获得了事件。
在我看来,这个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