简体   繁体   English

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

[英]WCF service proxy not setting “FieldSpecified” property

I've got a WCF DataContract that looks like the following: 我有一个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...
  }
}

When I add a reference to this service in Visual Studio, this proxy code is generated: 当我在Visual Studio中添加对此服务的引用时,将生成以下代理代码:

/// <remarks/>
[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;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Order=0)]
    public System.DateTime EditDate {
        get {
            return this.editDateField;
        }
        set {
            this.editDateField = value;
            this.RaisePropertyChanged("EditDate");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool EditDateSpecified {
        get {
            return this.editDateFieldSpecified;
        }
        set {
            this.editDateFieldSpecified = value;
            this.RaisePropertyChanged("EditDateSpecified");
        }
    }

    // code omitted for brevity...
}

As you can see, besides generating a backing property for EditDate , an additional <propertyname>Specified property is generated. 如您所见,除了为EditDate生成后备属性外, EditDate生成一个附加的<propertyname>Specified属性。 All good, except that when I do the following: 一切都很好,除了在执行以下操作时:

DataContractBase myDataContract = new DataContractBase();
myDataContract.EditDate = DateTime.Now;

new MyServiceClient.Update(new UpdateRequest(myDataContract));

the EditDate was not getting picked up by the endpoint of the service (does not appear in the transmitted XML). 服务的端点未获取EditDate (不会出现在传输的XML中)。

I debugged the code and found that, although I was setting EditDate , the EditDateSpecified property wasn't being set to true as I would expect; 我调试了代码,发现尽管设置了EditDate ,但EditDateSpecified属性并未如我EditDateSpecified地设置为true hence, the XML serializer was ignoring the value of EditDate , even though it's set to a valid value. 因此,即使XML EditDate设置为有效值,它也忽略了EditDate的值。

As a quick hack I modified the EditDate property to look like the following: 作为一个快速技巧,我将EditDate属性修改为如下所示:

   /// <remarks/>
    [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");
        }
    }

Now the code works as expected, but of course every time I re-generate the proxy, my modifications are lost. 现在代码可以按预期工作,但是当然每次我重新生成代理时,所做的修改都会丢失。 I could change the calling code to the following: 我可以将调用代码更改为以下内容:

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

new MyServiceClient.Update(new UpdateRequest(myDataContract));

but that also seems like a hack-ish waste of time. 但这似乎也浪费时间。

So finally, my question: does anyone have a suggestion on how to get past this unintuitive (and IMO broken) behavior of the Visual Studio service proxy generator, or am I simply missing something? 所以最后,我的问题是:是否有人对如何摆脱Visual Studio服务代理生成器的这种不直观(且IMO损坏)的行为提出建议,还是我只是缺少什么?

It might be a bit unintuitive (and caught me off guard and reeling, too!) - but it's the only proper way to handle elements that might or might not be specified in your XML schema. 这可能有点不直观(也使我措手不及!)-但这是处理XML模式中可能指定或可能未指定的元素的唯一正确方法。

And it also might seem counter-intuitive that you have to set the xyzSpecified flag yourself - but ultimately, this gives you more control, and WCF is all about the Four Tenets of SOA of being very explicit and clear about your intentions. 而且,您必须自己设置xyzSpecified标志似乎也很违反直觉-但是最终,这给了您更多的控制权,WCF就是关于SOA四个原则 ,它们非常明确并且清楚您的意图。

So basically - that's the way it is, get used to it :-) There's no way "past" this behavior - it's the way the WCF system was designed, and for good reason, too. 因此,基本上-这就是它的方式,请习惯它:-)不可能“粘贴”此行为-这是WCF系统的设计方式,也是有充分理由的。

What you always can do is catch and handle the this.RaisePropertyChanged("EditDate"); 您始终可以做的就是捕获并处理this.RaisePropertyChanged("EditDate"); event and set the EditDateSpecified flag in an event handler for that event. 事件并在该事件的事件处理程序中设置EditDateSpecified标志。

try this 尝试这个

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

This should omit the EditDateSpecified property since the field is specified as required 这应该省略EditDateSpecified属性,因为该字段是根据要求指定的

Rather than change the setters of the autogenerated code, you can use an extension class to 'autospecify' (bind the change handler event). 您可以使用扩展类来“自动指定”(绑定更改处理程序事件),而不是更改自动生成的代码的设置方法。 This could have two implementations -- a "lazy" one ( Autospecify ) using reflection to look for fieldSpecified based on the property name, rather than listing them all out for each class in some sort of switch statement like Autonotify : 这可能有两个实现-一个“懒惰的”实现( Autospecify ),它使用反射来基于属性名称查找fieldSpecified ,而不是在某种开关语句(如Autonotify为每个类列出所有这些实现:

Lazy

public static class PropertySpecifiedExtensions
{
    private const string SPECIFIED_SUFFIX = "Specified";

    /// <summary>
    /// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically set any xxxSpecified fields when a property is changed.  "Lazy" via reflection.
    /// </summary>
    /// <param name="entity">the entity to bind the autospecify event to</param>
    /// <param name="specifiedSuffix">optionally specify a suffix for the Specified property to set as true on changes</param>
    /// <param name="specifiedPrefix">optionally specify a prefix for the Specified property to set as true on changes</param>
    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);
            }
        };
    }

    /// <summary>
    /// Create a new entity and <see cref="Autospecify"/> its properties when changed
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="specifiedSuffix"></param>
    /// <param name="specifiedPrefix"></param>
    /// <returns></returns>
    public static T Create<T>(string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null) where T : INotifyPropertyChanged, new()
    {
        var ret = new T();
        ret.Autospecify(specifiedSuffix, specifiedPrefix);
        return ret;
    }
}

This simplifies writing convenience factory methods like: 这简化了编写便利工厂方法的过程,例如:

public partial class MyRandomClass 
{
    /// <summary>
    /// Create a new empty instance and <see cref="PropertySpecifiedExtensions.Autospecify"/> its properties when changed
    /// </summary>
    /// <returns></returns>
    public static MyRandomClass Create()
    {
        return PropertySpecifiedExtensions.Create<MyRandomClass>();
    }
}

A downside (other than reflection, meh) is that you have to use the factory method to instantiate your classes or use .Autospecify before (?) you make any changes to properties with specifiers. 缺点(除了反射,meh)是,您必须使用factory方法实例化您的类或使用.Autospecify 然后再使用(?)对说明符进行属性更改。

No Reflection 没有反思

If you don't like reflection, you could define another extension class + interface: 如果您不喜欢反射,则可以定义另一个扩展类+接口:

public static class PropertySpecifiedExtensions2
{
    /// <summary>
    /// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically call each class's <see cref="IAutoNotifyPropertyChanged.Autonotify"/> method on the property name.
    /// </summary>
    /// <param name="entity">the entity to bind the autospecify event to</param>
    public static void Autonotify(this IAutoNotifyPropertyChanged entity)
    {
        entity.PropertyChanged += (me, e) => ((IAutoNotifyPropertyChanged)me).WhenPropertyChanges(e.PropertyName);
    }

    /// <summary>
    /// Create a new entity and <see cref="Autonotify"/> it's properties when changed
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static T Create<T>() where T : IAutoNotifyPropertyChanged, new()
    {
        var ret = new T();
        ret.Autonotify();
        return ret;
    }
}

/// <summary>
/// Used by <see cref="PropertySpecifiedExtensions.Autonotify"/> to standardize implementation behavior
/// </summary>
public interface IAutoNotifyPropertyChanged : INotifyPropertyChanged
{
    void WhenPropertyChanges(string propertyName);
}

And then each class themselves defines the behavior: 然后每个类自己定义行为:

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

The downside to this is, of course, magic strings for property names making refactoring difficult, which you could get around with Expression parsing? 缺点当然是属性名称的魔术字符串使重构变得困难,您可以通过Expression解析来解决这个问题吗?

Here's a simple project that can modify the setters in generated WCF code for optional properties to automatically set the *Specified flags to true when setting the related value. 这是一个简单的项目,可以修改生成的WCF代码中的设置程序,以获取可选属性,以在设置相关值时自动将* Specified标志设置为true。

https://github.com/b9chris/WcfClean https://github.com/b9chris/WcfClean

Obviously there are situations where you want manual control over the *Specified flag so I'm not recommending it to everyone, but in most simple use cases the *Specified flags are just an extra nuisance and automatically setting them saves time, and is often more intuitive. 显然,有些情况下您需要手动控制* Specified标志,因此我不建议所有人使用它,但是在大多数简单的用例中,* Specified标志只是一个额外的麻烦,自动设置它们可以节省时间,并且通常更多直观。

Note that Mustafa Magdy's comment on another answer here will solve this for you IF you control the Web Service publication point. 请注意,如果您控制Web服务发布点,则Mustafa Magdy对此处的另一个答案的评论将为您解决此问题。 However, I usually don't control the Web Service publication and am just consuming one, and have to cope with the *Specified flags in some simple software where I'd like this automated. 但是,我通常不控制Web Service发布,而只使用其中的一个,并且不得不处理一些我希望实现自动化的简单软件中的* Specified标志。 Thus this tool. 因此这个工具。

Ian, Please ignore my previous answers, was explaining how to suck eggs. 伊恩,请忽略我以前的回答,在解释如何吸鸡蛋。 I've voted to delete them. 我已投票删除它们。

Could you tell me which version of Visual Studio you're using, please? 您能告诉我您使用的是哪个版本的Visual Studio吗?

In VS2005 client - in the generated code, I get the <property>Specified flags, but no event raised on change of values. 在VS2005客户端中-在生成的代码中,我得到了<property>Specified标志,但是在值更改时未引发任何事件。 To pass data I have to set the <property>Specified flag. 要传递数据,我必须设置<property>Specified标志。

In Visual Web Developer 2008 Express client - in the generated code, I get no <property>Specified flags, but I do get the event on change of value. 在Visual Web Developer 2008 Express客户端中-在生成的代码中,没有任何<property>Specified标志,但是在值更改时确实得到了事件。

Seems to me that this functionality has evolved and the Web Dev 2008 is closer to what you're after and is more intuitive, in that you don't need to set flags once you've set a value. 在我看来,此功能已经得到了发展,Web Dev 2008更接近您的需求,并且更加直观,因为您无需在设置值后就设置标志。

Bowthy 鲍西

Further information 更多信息

On the MSDN here 在MSDN 这里

In her answer, Shreesha explains that: Shreesha在回答中解释说:

"Specified" fields are only generated on optional parameters that are structs. “指定”字段仅在作为结构的可选参数上生成。 (int, datetime, decimal etc). (整数,日期时间,十进制等)。 All such variables will have additional variable generated with the name Specified. 所有此类变量都将具有名称为Specified的附加变量。

This is a way of knowing if a parameter is really passed between the client and the server. 这是一种了解参数是否确实在客户端和服务器之间传递的方法。

To elaborate, an optional integer, if not passed, would still have the dafault value of 0. How do you differentiate between this and the one that was actually passed with a value 0 ? 详细地说,一个可选的整数(如果未传递)仍将具有0的dafault值。如何区分该值和实际传递的值为0的整数? The "specified" field lets you know if the optional integer is passed or not. “指定”字段可让您知道是否传递了可选整数。 If the "specified" field is false, the value is not passed across. 如果“指定”字段为false,则不会传递该值。 If it true, the integer is passed. 如果为true,则传递整数。

so essentially, the only way to have these fields set is to set them manually, and if they aren't set to true for a field that has been set, then that field will be missed out in the SOAP message of the web-service call. 因此,实质上,设置这些字段的唯一方法是手动设置它们,如果对于已设置的字段未将它们设置为true,则该字段将在Web服务的SOAP消息中丢失。呼叫。

What I did in the end was build a method to loop through all the members of the object, and if the property has been set, and if there is a property called name _ Specified then set that to true. 最后,我要做的是建立一个遍历对象所有成员的方法,如果已设置该属性,并且是否有一个名为name _ Specified的属性,则将其设置为true。

Change proxy class properties to nullable type 将代理类属性更改为可为空的类型

ex : 例如:

bool? 布尔? confirmed 已确认

DateTime? 约会时间? checkDate checkDate

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM