WCF服务代理未设置"FieldSpecified"属性

Ian*_*emp 17 .net c# wcf

我有一个如下所示的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...
  }
}
Run Code Online (Sandbox Code Playgroud)

当我在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...
}
Run Code Online (Sandbox Code Playgroud)

如您所见,除了生成后备属性外EditDate,还会<propertyname>Specified生成其他属性.一切都很好,除了我做以下事情:

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

new MyServiceClient.Update(new UpdateRequest(myDataContract));
Run Code Online (Sandbox Code Playgroud)

EditDate没有得到该服务的端点回升(没有出现在所传输的XML).

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

作为一个快速黑客,我修改了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");
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

new MyServiceClient.Update(new UpdateRequest(myDataContract));
Run Code Online (Sandbox Code Playgroud)

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

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

mar*_*c_s 17

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

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

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

您始终可以做的是捕获并处理this.RaisePropertyChanged("EditDate");事件,并EditDateSpecified在该事件的事件处理程序中设置标志.

  • 但它仍然令人生气.***像老人一样抱怨*** (8认同)
  • 同意这是答案,不同意这是唯一的方式/好的设计.这是垃圾设计.他们需要提供一个选项,让这些设置自动设置在setter中,用于简单用例的属性. (3认同)

Nei*_*eil 11

试试这个

[DataMember(IsRequired=true)]
public DateTime EditDate { get; set; }
Run Code Online (Sandbox Code Playgroud)

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


drz*_*aus 6

您可以使用扩展类来"自动指定"(绑定更改处理程序事件),而不是更改自动生成代码的setter.这可能有两个实现 - 一个"懒"的one(Autospecify)使用反射来查找基于属性名称的fieldSpecified,而不是在某种switch语句中为每个类列出它们,如Autonotify:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

没有反思

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

当然,这对于属性名称的魔术字符串来说,重构难度很大,你可以通过Expression解析来解决这个问题吗?