Jacob Pettersson

Hi.

I'm having trouble getting a FileInfo object through to the client. First of all, if I add a operations contract on the server that returns a FileInfo object, it all works like a charm - The FileInfo object is received fine on the client-side.

But in my setup, the operations contract returns a custom class that COULD hold some FileInfo object inside.

Here's my setup:

[DataContract]

public class TypeDef

{

[DataMember(IsRequired = true)]

private string description;

[DataMember(IsRequired = true)]

private string name;

[DataMember(IsRequired = true)]

private object data;

public TypeDef(string name, string description, object data)

{

this.Name = name;

this.Description = description;

this.Data = data;

}

public string Description

{

get { return description; }

set { description = value; }

}

public string Name

{

get { return name; }

set { name = value; }

}

public object Data

{

get { return data; }

set { data = value; }

}

}

 

[DataContract]

public class IOObject

{

[DataMember(IsRequired = true)]

private string name;

[DataMember(IsRequired = true)]

public List<TypeDef> Types;

public IOObject(string name)

{

this.name = name;

Types = new List<TypeDef>();

}

public string Name

{

get

{

return name;

}

}

}

So basically, the IOObject holds a list of TypeDef objects that have a datafield which is of type Object.

The service contract:

[OperationContract]

IOObject[] GetIOObjectForTrigger(int id);

 

When I generate the client proxy with svcutil and consume the service I get the following error in Microsoft Service Trace Viewer (great tool btw!) (And assuming that a IOObject actually holds a FileInfo object ofcourse):

"There was an error while trying to serialize parameter http://tempuri.org/:GetIOObjectForTriggerResult. The InnerException message was 'Type 'System.IO.FileInfo' with data contract name 'FileInfo:http://schemas.datacontract.org/2004/07/System.IO' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details."

Well this sounded pretty straight forward. I added the [KnownType(typeof(System.IO.FileInfo))] to both the IOObject and TypeDef class and ran svcutil again.

The client is now able to create a proxy and call GetIOObjectForTriggerResult, but the value returned is not of the System.IO.FileInfo Type. Instead its a wierd type that is generated in the clientproxy from svcutil.exe. Here's the autogenerated code:

[System.Xml.Serialization.XmlIncludeAttribute(typeof(FileInfo))]

[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.30")]

[System.SerializableAttribute()]

[System.Diagnostics.DebuggerStepThroughAttribute()]

[System.ComponentModel.DesignerCategoryAttribute("code")]

[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schemas.datacontract.org/2004/07/System.IO")]

public partial class FileSystemInfo

{

private System.Xml.XmlElement[] anyField;

private System.Xml.XmlQualifiedName factoryTypeField;

/// <remarks/>

[System.Xml.Serialization.XmlAnyElementAttribute(Order=0)]

public System.Xml.XmlElement[] Any

{

get

{

return this.anyField;

}

set

{

this.anyField = value;

}

}

/// <remarks/>

[System.Xml.Serialization.XmlAttributeAttribute(Form=System.Xml.Schema.XmlSchemaForm.Qualified, Namespace="http://schemas.microsoft.com/2003/10/Serialization/")]

public System.Xml.XmlQualifiedName FactoryType

{

get

{

return this.factoryTypeField;

}

set

{

this.factoryTypeField = value;

}

}

}

/// <remarks/>

[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.30")]

[System.SerializableAttribute()]

[System.Diagnostics.DebuggerStepThroughAttribute()]

[System.ComponentModel.DesignerCategoryAttribute("code")]

[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schemas.datacontract.org/2004/07/System.IO")]

public partial class FileInfo : FileSystemInfo

{

}

This Type doesnt hold any of the information I need from the server.

Since I was able to send the FileInfo object alone (without including it in some custom class) it should be able to get my setup to work, right

How do I get the client to use a System.IO.FileInfo object and not the one from the svcutil generated proxy

I suspect that I'm using the KnownType attribute wrong, but so far i've been unable to figure it out myself.

Hope some of you can help me out.



Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

KjellSJ

The problem lies in the use of an 'object' as a DataMember. Try adding [ServiceKnownType(typeof(System.IO.FileInfo))] to the operation contract.

KjellSJ




Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

Jacob Pettersson

It didnt work.

The FileInfo object on the client-side is still the custom-one from the svc-generated proxy...

Would it make any difference if I created a Operations Contract that returned the object "directly"...

Something like this:

[OperationContract]

[ServiceKnownType(typeof(System.IO.FileInfo))]

Object GetMyObject(int id)





Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

Jacob Pettersson

I've been doing some tests on this one, and it seems that whenever I include a OperationContracts that return a DataSet, the svcutil.exe generates the custom FileInfo object described in my initial post.

When I remove all the OperationsContracts that use DataSets and generate the proxy, the normal System.IO.FileInfo type is used in the proxy.

In both cases the [ServiceKnownType(typeof(System.IO.FileInfo))] was set on the OperationsContract that uses IOObject.

I want to be able to use both the DataSet and IOObject (which can include a FileInfo object) object in the same service contract without having that bloody custom FileInfo object created in the svcutil generated proxy... shouldnt this be posssible





Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

Jacob Pettersson

Just to add to the story:

I was able to recreate this in a simple solution... Create the following service contract:

[ServiceContract]

public interface IService1

{

[OperationContract]

[ServiceKnownType(typeof(System.IO.FileInfo))]

object ReturnedObject1();

[OperationContract]

DataSet ReturnDataSet();

}

Implementation of ReturnedObject1() creates a new FileInfo Object and returns it. ReturnDataSet() just returns null.

Now if you use svcutil to generate a client proxy and config for this one, you'll see that a custom type, FileSystemInfo, was created by the util. When you call ReturnedObject1() you'll get a instance of this type without any information in it....

Now remove the operations contract with the dataset. Rerun svcutil. The generated proxy now uses the normal System.IO.FileInfo object and ReturnedObject1() returns the correct FileInfo data.

So my question would be...Is there any way to get these two operation contracts to work together





Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

Jacob Pettersson

Anyone got a clue to why this doesnt work



Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

Jacob Pettersson

I've created this simple solution to illustrate my problem:

http://www.pungeforsamlingen.dk/temp/WCFFileInfoExample.zip

I hope someone can help me figure out why the FileInfo and DataSet object type doesnt work in the same servicecontract





Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

joe zhou

The problem is actually in the svcutil's code generation. By default it will not re-create the system types you used on server side, it will only create a type with equivalent Data Contract mapping. If you want it to use the exact type you used on the server side, you need to use /r option to generate the proxy. detail can be found at http://msdn2.microsoft.com/en-us/library/aa347733.aspx

Joe





Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

Jacob Pettersson

hmmm, so if I want the proxy to use the System.IO.FileInfo object, i write svcutil.exe *something* /r:System.Info:IO



Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

joe zhou

since fileinfo is in mscorlib, I think you can use /r:mscorlib.dll or with full path to mscorlib

Jacob Pettersson wrote:
hmmm, so if I want the proxy to use the System.IO.FileInfo object, i write svcutil.exe *something* /r:System.Info:IO





Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

Jacob Pettersson

The util still generates the custom FileInfo object



Re: Windows Communication Foundation (Indigo) Sending a FileInfo object to client

joe zhou - MSFT

I just tried again and it indeed caused by dataset, because it is a IXmlSerializable type, so svcutil by default will import it as an xmlserializable type, and because if one operation has xmlserializable type, svcutil will generate all operations using xmlserializable types so it generate types for FileInfo. The solution will be using /r:system.data.dll so svcutil can reuse System.Data.Dataset. Below is the commandline I used on your sample

svcutil.exe /r:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll http://localhost/WCF

and here is the generated proxy

//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.42
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="IService1")]
public interface IService1
{

[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService1/ReturnedObject1", ReplyAction="http://tempuri.org/IService1/ReturnedObject1Response")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(System.IO.FileInfo))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(System.IO.FileSystemInfo))]
object ReturnedObject1();

[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService1/ReturnDataSet", ReplyAction="http://tempuri.org/IService1/ReturnDataSetResponse")]
System.Data.DataSet ReturnDataSet();
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IService1Channel : IService1, System.ServiceModel.IClientChannel
{
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class Service1Client : System.ServiceModel.ClientBase<IService1>, IService1
{

public Service1Client()
{
}

public Service1Client(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}

public Service1Client(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}

public Service1Client(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}

public Service1Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}

public object ReturnedObject1()
{
return base.Channel.ReturnedObject1();
}

public System.Data.DataSet ReturnDataSet()
{
return base.Channel.ReturnDataSet();
}
}

Jacob Pettersson wrote:
The util still generates the custom FileInfo object