客户端代理将在客户端和服务器上执行相同代码的前提是有缺陷的,因为 WCF 是基于接口的。我在下面的第 2 条中解释了这一点。
共享 WCF 接口和实现的规则
如果您想共享数据协定的实现,您需要将 RequestArray 类分解到一个类库中,该类库只包含数据协定类,可能还包括 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。另外,请参阅代码 cmets。如果有任何错误或任何不清楚的地方,请告诉我。
#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