I am currently developing a set of WCF (.NET 3.0) services to use as the
middle-tier of a distributed app. The services will not be publicly
available, and I have complete control over all three tiers of the
application.
Some of the services return strongly-typed DataTables for use by the UI
layer (ASP.NET 2.0) of the application. (SOA purists will object to this,
but we determined that this is the best choice for the data.) The
strongly-typed DataTables all follow the same model as the example listed
below.
The problem: I am using VisualStudio 2005 to generate a service reference
(client) class. I will be providing a wrapper class around the generated
client, compliant with my IComponentProviderListService, for consumers of the
service to use. The generated interface includes the following comment:
"Parameter 'ListEquipmentResult' requires additional schema information that
cannot be captured using the parameter mode." The specific attribute is
'System.Xml.Serialization.XmlElementAttribute'. What does this mean? How do
I resolve it? In the generated ListEquipmentResponse and
ListEquipmentResponseListEquipmentResult classes (shown below), where am I
supposed to see something that looks like a serialized DataTable that I can
deserialize?
My interface looks like this:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IComponentProviderListService
{
. . .
[OperationContract(IsInitiating = false, IsTerminating = false)]
EquipmentList ListEquipment(bool availableOnly);
. . .
}
The analogous method in the proxy looks like
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ComponentService.Proxy.ComponentProviderListService.IComponentProviderListService" +
"", SessionMode=System.ServiceModel.SessionMode.Required)]
public interface IComponentProviderListService
{
// CODEGEN: Parameter 'ListEquipmentResult' requires additional schema
information that cannot be captured using the parameter mode. The specific
attribute is 'System.Xml.Serialization.XmlElementAttribute'.
[System.ServiceModel.OperationContractAttribute(IsInitiating=false,
Action="http://tempuri.org/IComponentProviderListService/ListEquipment",
ReplyAction="http://tempuri.org/IComponentProviderListService/ListEquipmentResponse")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
ComponentService.Proxy.ComponentProviderListService.ListEquipmentResponse
ListEquipment(ComponentService.Proxy.ComponentProviderListService.ListEquipmentRequest request);
}
The generated ListEquipmentResponse looks like
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="ListEquipmentResponse", WrapperNamespace="http://tempuri.org/", IsWrapped=true)]
public partial class ListEquipmentResponse
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://tempuri.org/", Order=0)]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public
ComponentService.Proxy.ComponentProviderListService.ListEquipmentResponseListEquipmentResult ListEquipmentResult;
public ListEquipmentResponse()
{
}
public
ListEquipmentResponse(ComponentService.Proxy.ComponentProviderListService.ListEquipmentResponseListEquipmentResult ListEquipmentResult)
{
this.ListEquipmentResult = ListEquipmentResult;
}
}
The generated ListEquipmentResponseListEquipmentResult looks like
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.30")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true,
Namespace="http://tempuri.org/")]
public partial class ListEquipmentResponseListEquipmentResult
{
private System.Xml.XmlElement[] anyField;
private System.Xml.XmlElement any1Field;
private string namespaceField;
private string tableTypeNameField;
public ListEquipmentResponseListEquipmentResult()
{
this.namespaceField = "Components.DataStructures";
this.tableTypeNameField = "EquipmentList";
}
[System.Xml.Serialization.XmlAnyElementAttribute(Namespace="http://www.w3.org/2001/XMLSchema", Order=0)]
public System.Xml.XmlElement[] Any
{
get{return this.anyField;}
set{this.anyField = value;}
}
[System.Xml.Serialization.XmlAnyElementAttribute(Namespace="urn:schemas-microsoft-com:xml-diffgram-v1", Order=1)]
public System.Xml.XmlElement Any1
{
get{return this.any1Field;}
set{this.any1Field = value;}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string @namespace
{
get{return this.namespaceField;}
set{this.namespaceField = value;}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string tableTypeName
{
get{return this.tableTypeNameField;}
set{this.tableTypeNameField = value;}
}
}
The EquipmentList (strongly typed DataTable class)
[XmlSchemaProvider("GetSchema")]
[Serializable]
public class EquipmentList : DataTable, IXmlSerializable, IEnumerable
{
private static string _tableName = "EquipmentList";
private static string _namespace = "Components.DataStructures";
private static XmlSchema _schema;
// Private DataColumn variables declared and initialized here
public EquipmentList():base(_tableName, _namespace)
{
this.TableName = _tableName;
this.BeginInit();
Initialize();
this.EndInit();
}
protected EquipmentList(string tableName, string nameSpace) :
base(tableName, nameSpace)
{
this.TableName = _tableName;
this.BeginInit();
Initialize();
this.EndInit();
}
public int Count
{
get{return this.Rows.Count;}
}
// Misc. methods for adding and retrieving strongly typed rows omitted
// Properties to expose DataColumns omitted
private void Initialize()
{
// Set DataColumn properties and add them to base.Columns collection
if (_schema == null)
{
GetTableSchema();
_schema.Id = _tableName;
}
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
throw new System.NotImplementedException();
}
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
base.ReadXml(reader);
}
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
base.WriteXml(writer, XmlWriteMode.DiffGram);
}
private void GetTableSchema()
{
System.IO.MemoryStream stream = new System.IO.MemoryStream();
this.WriteXmlSchema(new System.Xml.XmlTextWriter(stream, null));
stream.Position = 0;
_schema = XmlSchema.Read(new System.Xml.XmlTextReader(stream),
null);
}
public static XmlSchemaComplexType GetSchema(XmlSchemaSet xss)
{
XmlSchemaComplexType type = new XmlSchemaComplexType();
XmlSchemaSequence sequence = new XmlSchemaSequence();
if (_schema == null)
{
EquipmentList list = new EquipmentList();
}
xss.Add(_schema);
XmlSchemaAny any1 = new XmlSchemaAny();
any1.Namespace = "http://www.w3.org/2001/XMLSchema";
any1.MinOccurs = new decimal(0);
any1.MaxOccurs = decimal.MaxValue;
any1.ProcessContents =
System.Xml.Schema.XmlSchemaContentProcessing.Lax;
sequence.Items.Add(any1);
System.Xml.Schema.XmlSchemaAny any2 = new
System.Xml.Schema.XmlSchemaAny();
any2.Namespace = "urn:schemas-microsoft-com:xml-diffgram-v1";
any2.MinOccurs = new decimal(1);
any2.ProcessContents =
System.Xml.Schema.XmlSchemaContentProcessing.Lax;
sequence.Items.Add(any2);
System.Xml.Schema.XmlSchemaAttribute attribute1 = new
System.Xml.Schema.XmlSchemaAttribute();
attribute1.Name = "namespace";
attribute1.FixedValue = _namespace;
type.Attributes.Add(attribute1);
System.Xml.Schema.XmlSchemaAttribute attribute2 = new
System.Xml.Schema.XmlSchemaAttribute();
attribute2.Name = "tableTypeName";
attribute2.FixedValue = _tableName;
type.Attributes.Add(attribute2);
type.Particle = sequence;
return type;
}
public IEnumerator GetEnumerator()
{
return this.Rows.GetEnumerator();
}
}// END CLASS DEFINITION EquipmentList
The associated strongly typed row class looks like this
public class EquipmentListRow : DataRow
{
private EquipmentList _table;
internal EquipmentListRow(DataRowBuilder rb) : base(rb)
{
this._table = ((EquipmentList)(this.Table));
}
// Properties to set column values omitted
}
From your description, you have a WCF service which use a service method
(operation) to return a typed DataTAble. However, in the client-side
generated proxy, you get some problem with the generated proxy class of the
DataTable return object, correct?
Based on my experience, as for WCF client-side proxy, if you contains
complex type in your WCF service side, it will automatically create a proxy
class corresponding to it(like a simple wrapper class that has the same
field/properties set). However, for those type such as Typed
dataset/datatable or collection types, you may need to reuse the same types
as what used in the WCF service methods. For example, you can define the
Typed dataset/datatable in a separate class library project, and both the
WCF service and client application(which contains the proxy) can reuse the
same type (in the class library assembly).
#Type sharing in WCF service reference
http://blogs.msdn.com/lifenglu/archive/2007/05/09/type-sharing-in-wcf-servic
e-reference.aspx
#Sharing WCF Collection Types between Service and Client
http://www.codeproject.com/KB/WCF/WCFCollectionTypeSharing.aspx
the above two reference has provided some guide on how to share/reuse types
between WCF service and client.
I have performed some tests on my side. Here is the steps to share the
DataTable types:
** create the DataTable in a separate class library project/assembly
** reference the DataTable class in both WCF service and client proxy
project
** and the most important part is the proxy generation. On my side, I use
VS 2008 which is quite simple to share type, since VS 2008 "Add Service
Reference" will be default reuse any types that are used in WCF service and
also referenced by the client-side project(which use the wcf proxy).
I noticed that you're using VS 2005, correct? Is there any VS 2008
available?(if so you can use it to help generate the proxy) Or as
mentioned in the above aritlce, you can also use 'svcutil.exe' to generate
proxy class which reuse existing types:
#ServiceModel Metadata Utility Tool (Svcutil.exe)
http://msdn.microsoft.com/en-us/library/aa347733.aspx
this tool is available if you have .NET 3.0 and windows sdk 6 installed.
As document said, svcutil.exe use "/reference" command line option to let
you specify the assembly from which you want to share the types. thus, when
generating the proxy, if any types(used in the WCF service) is existing in
the specificed reference assembly, the proxy will reuse the type from the
assembly instead of creating a new wrapper class.
If you have anything unclear, please feel free to let me know.
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
msd...@microsoft.com.
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.
Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
From: =?Utf-8?B?UGF1bA==?= <Pa...@discussions.microsoft.com>
Subject: WCF Client Generation Issue
Date: Fri, 9 May 2008 10:00:01 -0700
Thank you for your reply. I had already implemented the first two steps you
recommend (the strongly typed DataTables in their own library and referencing
that library in the service and proxy projects). Between my posting the
question and your reply today, I had experimented with some different
attributes and seemed to get closer to what I needed. I am hopeful that the
additional information you provided about the proxy generation will get me
the rest of the way there. I will post the final solution here, assuming I
get it to work, or will report that something is still amiss.
It would be useful to have a reference available somewhwere (KB on the
Microsoft site) with an explanation of all messages that the code generation
tools can produce, including their causes and resolutions. That might have
saved the need to post anything here at all.
Thank You
Thanks for your reply. I'm glad that you've already got progress on your
side. Sure, if you've got any new results, welcome to post them here and
share with us.
BTW, for your further comments about the WCF document and reference
resources, I agree that for some specific topcs or features, the related
information is not quite sufficient and centralized that make it a bit
difficult to locate.
You can find more information about WCF on MSDN WCF center or WCF community
center:
http://netfx3.com/content/WCFHome.aspx
http://msdn.microsoft.com/en-us/netframework/aa663324.aspx
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
msd...@microsoft.com.
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
From: =?Utf-8?B?UGF1bA==?= <Pa...@discussions.microsoft.com>
References: <8FD9D100-819F-4BB5...@microsoft.com>
<nWZpTtmt...@TK2MSFTNGHUB02.phx.gbl>
Subject: RE: WCF Client Generation Issue
Date: Thu, 15 May 2008 07:34:02 -0700