我有一个如下所示的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在该事件的事件处理程序中设置标志.
Nei*_*eil 11
试试这个
[DataMember(IsRequired=true)]
public DateTime EditDate { get; set; }
Run Code Online (Sandbox Code Playgroud)
这应该省略EditDateSpecified属性,因为字段是根据需要指定的
您可以使用扩展类来"自动指定"(绑定更改处理程序事件),而不是更改自动生成代码的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解析来解决这个问题吗?
| 归档时间: |
|
| 查看次数: |
14768 次 |
| 最近记录: |