我对WCF有点新意,我不认为我完全理解DataContracts的交易.我有这个'RequestArray'类:
[DataContract] public class RequestArray { private int m_TotalRecords; private RequestRecord[] m_Record; [System.Xml.Serialization.XmlElement] [DataMember] public RequestRecord[] Record { get { return m_Record; } } [DataMember] public int TotalRecords { get { return m_TotalRecords; } set { if (value > 0 && value <= 100) { m_TotalRecords = value; m_Record = new RequestRecord[value]; for (int i = 0; i < m_TotalRecords; i++) m_Record[i] = new RequestRecord(); } } } }
这个想法是当客户端说requestArray.TotalRecords=6;
Record阵列将被分配和初始化时(我意识到我隐藏了一个分配后面的实现,这是我无法控制的).
问题是当客户端执行此操作时,不会调用TotalRecord的设置代码,服务中的断点也会确认.相反,已经生成了某种通用的setter而是被调用.如何让客户端使用我的setter?
编辑:看起来我并没有完全了解[DataContract]的工作原理,但客户端不会执行此代码是有道理的.就像我在评论中提到的那样,如果我'手动'执行setter的工作,我会看到设置代码在我调用服务函数时正确执行.
序列化我还不确定.RequestRecord []数组的内容不会被转移.Record类有setter/getters,我觉得我需要一个辅助函数来帮助它序列化整个类.
感谢你的帮助!
客户端代理将在客户端和服务器上执行相同代码的前提是有缺陷的,因为WCF基于接口.我在下面的子弹#2中解释了这一点.
如果要共享数据协定的实现,则需要将RequestArray类分解为一个类库,该类包含NOTHING但数据协定类,包括可能还有RequestRecord类.
我遵守的规则:
将所有数据合同(100%)分组到一个或多个程序集中,没有例外.
将所有服务合同分组到一个或多个程序集中.
将所有服务类型(即实现服务合同的类)分组到一个或多个程序集中.
将所有客户机通道代理(即,调用服务协定接口上定义的方法的类)分组到一个或多个程序集中.
在通用框架中,所有客户端软件都作为WCF服务运行(我避免双工连接),合并规则2,3和4是安全的,这样服务合同,服务类型和通道代理就可以组合成一个程序集.
将接口分离为更灵活的依赖关系链的主要原因是可以将有限的一组程序集部署到客户端,而不会暴露不必要的和可能的专有实现细节.另一个原因是它使重构变得如此简单,特别是在您希望通过继承或委派实现或扩展通用框架的情况下.
RequestArray的代码有一些大的问题......
当反序列化DataContract实例时,setter逻辑将覆盖m_Record数组变量的任何修改元素.这违反了反序列化原则.
Record属性将无法反序列化,因为RequestArray类上的Record属性是只读的(因为它没有setter). 通常,我发现对于DataContract类,只读属性的最佳方法只是一种方法. 养成处理数据合同的习惯是一个坏主意,就像它们不仅仅是比特桶一样.属性基本上是动态创建专门用于数据序列化和反序列化的接口定义. 我认为将线上的数据视为对象是错误的.相反,它是一种选择性的方式来指定需要通过线路持久存在的对象的相关数据部分.
如果TotalRecords属性结束(正确)只允许设置m_TotalRecords变量,则它会变得危险,因为它将完全独立于内部数组.为了让我在我的示例代码(下面)中可以接受这个工作,我不得不屏蔽套件if (m_TotalRecords == 0)
.在我保存以供将来使用的示例代码中,我完全注释掉TotalRecords属性,但是我留下m_TotalRecords只是为了说明私有对象实际上是通过线路保留的.
我改编了bendewey的示例代码(谢谢!)并提出了这个完整的测试.注意:我必须定义RequestRecord.另外,请参阅代码注释.如果有任何错误或任何不清楚的地方,请告诉我.
#region WCFDataContractTest [DataContract] // The enclosed type needs to also be attributed for WCF public class RequestRecord { public RequestRecord() { } [DataMember] // This is CRUCIAL, otherwise the Name property will not be preserved. public string Name { get; set; } } [DataContract] // Encloses the RequestRecord type public class RequestArray { private int m_TotalRecords; // should be for internal bookkeeping only private RequestRecord[] m_Record; [System.Xml.Serialization.XmlElement] [DataMember] public RequestRecord[] Record { get { return m_Record; } // deserialization will not work without the set set { m_Record = value; } } [DataMember] // is not really needed public int TotalRecords { get { return m_TotalRecords; } set { if (m_TotalRecords == 0) m_TotalRecords = value; } } // The constructor is not called by the deserialization mechanism, // therefore this is the right place to specify the array size and to // perform the array initialization. public RequestArray(int totalRecords) { if (totalRecords > 0 && totalRecords <= 100) { m_TotalRecords = totalRecords; m_Record = new RequestRecord[totalRecords]; for (int i = 0; i < m_TotalRecords; i++) m_Record[i] = new RequestRecord() { Name = "Record #" + i.ToString() }; m_TotalRecords = totalRecords; } else m_TotalRecords = 0; } } public static void TestWCFDataContract() { var serializer = new DataContractSerializer(typeof(RequestArray)); var test = new RequestArray(6); Trace.WriteLine("Array contents after 'new':"); for (int i = 0; i < test.Record.Length; i++) Trace.WriteLine("\tRecord #" + i.ToString() + " .Name = " + test.Record[i].Name); //Modify the record values... for (int i = 0; i < test.Record.Length; i++) test.Record[i].Name = "Record (Altered) #" + i.ToString(); Trace.WriteLine("Array contents after modification:"); for (int i = 0; i < test.Record.Length; i++) Trace.WriteLine("\tRecord #" + i.ToString() + " .Name = " + test.Record[i].Name); using (var ms = new MemoryStream()) { serializer.WriteObject(ms, test); ms.Flush(); ms.Position = 0; var newE = serializer.ReadObject(ms) as RequestArray; Trace.WriteLine("Array contents upon deserialization:"); for (int i = 0; i < newE.Record.Length; i++) Trace.WriteLine("\tRecord #" + i.ToString() + " .Name = " + newE.Record[i].Name); } } #endregion
运行TestWCFDataContract后,此示例程序的列表是:
'new'后的数组内容:
Record #0 .Name = Record #0 Record #1 .Name = Record #1 Record #2 .Name = Record #2 Record #3 .Name = Record #3 Record #4 .Name = Record #4 Record #5 .Name = Record #5
修改后的数组内容:
Record #0 .Name = Record (Altered) #0 Record #1 .Name = Record (Altered) #1 Record #2 .Name = Record (Altered) #2 Record #3 .Name = Record (Altered) #3 Record #4 .Name = Record (Altered) #4 Record #5 .Name = Record (Altered) #5
反序列化时的数组内容:
Record #0 .Name = Record (Altered) #0 Record #1 .Name = Record (Altered) #1 Record #2 .Name = Record (Altered) #2 Record #3 .Name = Record (Altered) #3 Record #4 .Name = Record (Altered) #4 Record #5 .Name = Record (Altered) #5