WSDL generation issue

Posts   
 
    
NMackay
User
Posts: 138
Joined: 31-Oct-2011
# Posted on: 24-Sep-2013 10:30:10   

I know I've opened helpdesk threads about this and discussed it before but is there any way you can serialize ITypedview2 over WCF without causing the WSDL generation of the service to fail in the browser?

Currently I can pass my typedview's back to the client using this interface:


namespace MyNamespace.DataServices.Legacy
{
    [ServiceContract()]
    [ServiceKnownType(typeof(VOwnerTypedView))] // -- view
    [ServiceKnownType(typeof(VesselEntity))]
    [ServiceKnownType(typeof(VesstatusEntity))]
    [ServiceKnownType(typeof(VestypeEntity))]
    [ServiceKnownType(typeof(VessaccompEntity))]
    [ServiceKnownType(typeof(AcccompanyEntity))]
    [ServiceKnownType(typeof(EntityCollection))]
    public interface IVesselService
    {
        ////  Single Entity fetches 
        [OperationContract()]
        IEntity2 GetVessel(string VesID);

        ////  Entity Collection fetches 
        [OperationContract()]
        IEntityCollection2 GetVesselByType(string VtyID);

        [OperationContract()]
        IEntityCollection2 GetVesselType();

        ////  View fetches 
        [OperationContract()]
        VOwnerTypedView GetVesselOwners();

        ////  SP fetches 
        [OperationContract()]
        DataTable GetVesselByStatus(string StatusID);
    }
}

Works nicely in my client service:


 public VOwnerTypedView GetVesselOwners()
        {

            IVesselService client = _veschannelFactory.CreateChannel();
            try
            {
                VOwnerTypedView toReturn = (VOwnerTypedView)client.GetVesselOwners();
                ((IClientChannel)client).Close();
                return toReturn;
            }

            catch (FaultException fe)
            {
                ((IClientChannel)client).Abort();

Unfortunately when you ask to see the service in the browser you get the following:


An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.InvalidOperationException: An exception was thrown in a call to a WSDL export extension: System.ServiceModel.Description.DataContractSerializerOperationBehavior
 contract: http://tempuri.org/:IVesselService ----> System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Data.XmlTreeGen.FindTargetNamespace(DataTable table)
   at System.Data.XmlTreeGen.HandleColumn(DataColumn col, XmlDocument dc, XmlElement schema, Boolean fWriteOrdinal)
   at System.Data.XmlTreeGen.HandleTable(DataTable table, XmlDocument dc, XmlElement schema, Boolean genNested)
   at System.Data.XmlTreeGen.SchemaTree(XmlDocument xd, DataTable dt)
   at System.Data.XmlTreeGen.Save(DataTable dt, XmlWriter xw)
   at System.Data.DataTable.GetSchema()
   at System.Data.DataTable.System.Xml.Serialization.IXmlSerializable.GetSchema()
   at System.Runtime.Serialization.SchemaExporter.InvokeGetSchemaMethod(Type clrType, XmlSchemaSet schemas, XmlQualifiedName stableName)
   at System.Runtime.Serialization.SchemaExporter.ExportXmlDataContract(XmlDataContract dataContract)
   at System.Runtime.Serialization.SchemaExporter.ExportDataContract(DataContract dataContract)
   at System.Runtime.Serialization.SchemaExporter.Export()
   at System.Runtime.Serialization.XsdDataContractExporter.Export()
   at System.Runtime.Serialization.XsdDataContractExporter.Export(Type type)
   at System.ServiceModel.Description.MessageContractExporter.ExportType(Type type, String partName, String operationName, XmlSchemaType& xsdType)
   at System.ServiceModel.Description.DataContractSerializerMessageContractExporter.ExportBody(Int32 messageIndex, Object state)
   at System.ServiceModel.Description.MessageContractExporter.ExportMessage(Int32 messageIndex, Object state)
   at System.ServiceModel.Description.MessageContractExporter.ExportMessageContract()
   at System.ServiceModel.Description.DataContractSerializerOperationBehavior.System.ServiceModel.Description.IWsdlExportExtension.ExportContract(WsdlExporter exporter, WsdlContractConversionContext contractContext)
   at System.ServiceModel.Description.WsdlExporter.CallExtension(WsdlContractConversionContext contractContext, IWsdlExportExtension extension)
   --- End of inner ExceptionDetail stack trace ---
   at System.ServiceModel.Description.ServiceMetadataBehavior.MetadataExtensionInitializer.GenerateMetadata()
   at System.ServiceModel.Description.ServiceMetadataExtension.EnsureInitialized()
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.InitializationData.InitializeFrom(ServiceMetadataExtension extension)
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.GetInitData()
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.TryHandleDocumentationRequest(Message httpGetRequest, String[] queries, Message& replyMessage)
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.ProcessHttpRequest(Message httpGetRequest)
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.Get(Message message)
   at SyncInvokeGet(Object , Object[] , Object[] )
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)


I know this is probably down to WCF not knowing about ITypedView2 but this isn't an acceptable compromise as it would never be signed of in that state. I can use DataTable but that sucks a bit in a strongly typed interface and open to abuse.

Is there a nice way to project the DataTable to the correct TypedView or can issue the be resolved in anyway? Passing back DataTable is a fudge and the more we use it the less we like it.

I thought I'd ask anyway in case anything has changed since I last asked simple_smile

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 24-Sep-2013 22:56:04   

In this case the browser acts as the WCF client, and since it knows nothing about that interface, it won't be able to deserialize the typedView.

NMackay
User
Posts: 138
Joined: 31-Oct-2011
# Posted on: 25-Sep-2013 09:45:35   

Walaa wrote:

In this case the browser acts as the WCF client, and since it knows nothing about that interface, it won't be able to deserialize the typedView.

Walaa,

Thanks for your response.

I know that, I was looking for workarounds as a stack trace isn't a great indicator that the service is running fine.

Is there a nice way to project the datatable back to TypedView2 on the client service interface? as it is, iTypedview2 is pretty much useless in a WCF service which is a shame as we'd like to use views and don't really want to convert them as entities.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 25-Sep-2013 10:49:19   

TypedViews are datatables, perhaps that will work as a 'known' type?

Frans Bouma | Lead developer LLBLGen Pro
NMackay
User
Posts: 138
Joined: 31-Oct-2011
# Posted on: 25-Sep-2013 11:14:53   

Otis wrote:

TypedViews are datatables, perhaps that will work as a 'known' type?

Hi Frans,

I tried datatable and while it all communicates fine and the view is passed back the same issue persists, it doesn't seem to know about DataTable.


namespace VShips.DataServices.Legacy
{
    [ServiceContract()]
    [ServiceKnownType(typeof(DataTable))]
    [ServiceKnownType(typeof(VesselEntity))]
    [ServiceKnownType(typeof(EntityCollection))]
    public interface IVesselService
    {

        [OperationContract()]
        IEntity2 GetVessel(string VesID);

        [OperationContract()]
        VOwnerTypedView GetVesselOwners();

    }

}


An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.InvalidOperationException: An exception was thrown in a call to a WSDL export extension: System.ServiceModel.Description.DataContractSerializerOperationBehavior
 contract: http://tempuri.org/:IVesselService ----> System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Data.XmlTreeGen.FindTargetNamespace(DataTable table)
   at System.Data.XmlTreeGen.HandleColumn(DataColumn col, XmlDocument dc, XmlElement schema, Boolean fWriteOrdinal)
   at System.Data.XmlTreeGen.HandleTable(DataTable table, XmlDocument dc, XmlElement schema, Boolean genNested)
   at System.Data.XmlTreeGen.SchemaTree(XmlDocument xd, DataTable dt)
   at System.Data.XmlTreeGen.Save(DataTable dt, XmlWriter xw)
   at System.Data.DataTable.GetSchema()
   at System.Data.DataTable.System.Xml.Serialization.IXmlSerializable.GetSchema()
   at System.Runtime.Serialization.SchemaExporter.InvokeGetSchemaMethod(Type clrType, XmlSchemaSet schemas, XmlQualifiedName stableName)
   at System.Runtime.Serialization.SchemaExporter.ExportXmlDataContract(XmlDataContract dataContract)
   at System.Runtime.Serialization.SchemaExporter.ExportDataContract(DataContract dataContract)
   at System.Runtime.Serialization.SchemaExporter.Export()
   at System.Runtime.Serialization.XsdDataContractExporter.Export()
   at System.Runtime.Serialization.XsdDataContractExporter.Export(Type type)
   at System.ServiceModel.Description.MessageContractExporter.ExportType(Type type, String partName, String operationName, XmlSchemaType& xsdType)
   at System.ServiceModel.Description.DataContractSerializerMessageContractExporter.ExportBody(Int32 messageIndex, Object state)
   at System.ServiceModel.Description.MessageContractExporter.ExportMessage(Int32 messageIndex, Object state)
   at System.ServiceModel.Description.MessageContractExporter.ExportMessageContract()
   at System.ServiceModel.Description.DataContractSerializerOperationBehavior.System.ServiceModel.Description.IWsdlExportExtension.ExportContract(WsdlExporter exporter, WsdlContractConversionContext contractContext)
   at System.ServiceModel.Description.WsdlExporter.CallExtension(WsdlContractConversionContext contractContext, IWsdlExportExtension extension)
   --- End of inner ExceptionDetail stack trace ---
   at System.ServiceModel.Description.WsdlExporter.CallExtension(WsdlContractConversionContext contractContext, IWsdlExportExtension extension)
   at System.ServiceModel.Description.WsdlExporter.CallExportContract(WsdlContractConversionContext contractContext)
   at System.ServiceModel.Description.WsdlExporter.ExportContract(ContractDescription contract)
   at System.ServiceModel.Description.WsdlExporter.ExportEndpoint(ServiceEndpoint endpoint, XmlQualifiedName wsdlServiceQName, BindingParameterCollection bindingParameters)
   at System.ServiceModel.Description.WsdlExporter.ExportEndpoints(IEnumerable`1 endpoints, XmlQualifiedName wsdlServiceQName, BindingParameterCollection bindingParameters)
   at System.ServiceModel.Description.ServiceMetadataBehavior.MetadataExtensionInitializer.GenerateMetadata()
   at System.ServiceModel.Description.ServiceMetadataExtension.EnsureInitialized()
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.InitializationData.InitializeFrom(ServiceMetadataExtension extension)
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.GetInitData()
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.TryHandleDocumentationRequest(Message httpGetRequest, String[] queries, Message& replyMessage)
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.ProcessHttpRequest(Message httpGetRequest)
   at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.Get(Message message)
   at SyncInvokeGet(Object , Object[] , Object[] )
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)


I thought perhaps get round it be marking the methods that return a ITypedView2 not for export but still be available but there's nothing in WCF to allow this.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 25-Sep-2013 18:31:15   

Try letting Please let the method return a dataTable. i.e. change the return type in the signature.

NMackay
User
Posts: 138
Joined: 31-Oct-2011
# Posted on: 25-Sep-2013 18:37:46   

Walaa wrote:

Try letting Please let the method return a dataTable. i.e. change the return type in the signature.

Hi,

I know that works but I want to return the actual TypedView type for the reasons listed in my original post.


namespace VShips.DataServices.Legacy
{
    [ServiceContract()]
    [ServiceKnownType(typeof(VesselEntity))]
    [ServiceKnownType(typeof(VesstatusEntity))]
    [ServiceKnownType(typeof(VestypeEntity))]
    [ServiceKnownType(typeof(VessaccompEntity))]
    [ServiceKnownType(typeof(AcccompanyEntity))]
    [ServiceKnownType(typeof(EntityCollection))]
    public interface IVesselService
    {

        ////  Single Entity fetches 
        [OperationContract()]
        [FaultContractAttribute(typeof(VServiceFault))]
        IEntity2 GetVessel(string VesID);

        ////  Entity Collection fetches 
        [OperationContract()]
        [FaultContractAttribute(typeof(VServiceFault))]
        IEntityCollection2 GetVesselByType(string VtyID);

        [OperationContract()]
        [FaultContractAttribute(typeof(VServiceFault))]
        IEntityCollection2 GetVesselType();

        ////  View fetches 
        [OperationContract()]
        [FaultContractAttribute(typeof(VServiceFault))]
        VOwnerTypedView GetVesselOwners();

        ////  SP fetches 
        [OperationContract()]
        [FaultContractAttribute(typeof(VServiceFault))]
        DataTable GetVesselByStatus(string StatusID);
    }

}

I'd I'm passing back DataTables what's the point of using the ORM? confused

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 25-Sep-2013 18:51:18   

You are going to return a TypedView, but since it's supertype is DataTable, you can use that in the signature, and cast to the TypedView type at the client side.

In general simple DTO's are the recommended containers to be use in WCF and messaging in general, not complex types, that's IMHO.

NMackay
User
Posts: 138
Joined: 31-Oct-2011
# Posted on: 26-Sep-2013 09:56:26   

Walaa wrote:

You are going to return a TypedView, but since it's supertype is DataTable, you can use that in the signature, and cast to the TypedView type at the client side.

In general simple DTO's are the recommended containers to be use in WCF and messaging in general, not complex types, that's IMHO.

Fine but the product doesn't support POCO/DTO generation out of the box disappointed The documentation should make clear that using the native LLBLGen types has restrictions with WCF.

Datatables over WCF is hardly efficient, for a read only view of data a lot of junk is serialised that isn't needed.

In theory we'd like to use entities for obvious reasons and DTO's for views. Using Typedview's over WCF 99% works, that's the frustrating part disappointed

Hey ho.

NMackay
User
Posts: 138
Joined: 31-Oct-2011
# Posted on: 26-Sep-2013 10:17:35   

Is there a nicer way of casting back to the correct typedview rather than using this?


public VOwnerTypedView GetVesselOwners()
        {

            IVesselService client = _veschannelFactory.CreateChannel();
            try
            {
                DataTable toReturn = client.GetVesselOwners();

                VOwnerTypedView tv = new VOwnerTypedView();
                tv.Merge(toReturn);

                ((IClientChannel)client).Close();
                return tv;
            }

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 26-Sep-2013 12:29:21   

NMackay wrote:

Walaa wrote:

You are going to return a TypedView, but since it's supertype is DataTable, you can use that in the signature, and cast to the TypedView type at the client side.

In general simple DTO's are the recommended containers to be use in WCF and messaging in general, not complex types, that's IMHO.

Fine but the product doesn't support POCO/DTO generation out of the box disappointed The documentation should make clear that using the native LLBLGen types has restrictions with WCF.

What restrictions, exactly? That one can't view them in a browser? They work fine if you specify the types as known types. That's clearly documented. It's a bit of a pain to define the types in the first place, but that's how WCF and it's farm of inefficient xml serializers work.

Datatables over WCF is hardly efficient, for a read only view of data a lot of junk is serialised that isn't needed.

A typedview will use its parent class' xml serialization, so you won't get other XML from a typedview than what a datatable will produce. It might be some value has to be set on the DataTable to make this work.

Though I think you run into this problem: http://blogs.msdn.com/b/lifenglu/archive/2007/08/01/passing-datatable-across-web-wcf-services.aspx (as it's the same: a typedview is a typed datatable, without a dataset)

About the other question: unfortunately, no, you have to merge.

What you could do as well is create a DTO class using a template, from the TypedView and use it in a projection in e.g. queryspec: you then define the query on the typedview, but project the results in instances of the DTO class and return that.

Frans Bouma | Lead developer LLBLGen Pro
NMackay
User
Posts: 138
Joined: 31-Oct-2011
# Posted on: 26-Sep-2013 12:47:13   

They work fine if you specify the types as known types

Well as long as you don't mind seeing a stack trace for your service page then yes, they do simple_smile

It might be some value has to be set on the DataTable to make this work.

That's what I was thinking but I'm not sure which value. I did read about the same restriction elsewhere, I was just hoping you had found a workaround at some stage.

Out of interest, why are Typedview's based on datatable rather than just been a lightweight collection of entities (basic poco classes since it's read-only)? I've read so many articles saying database over WCF is bad practice although the performance seems fine.

We don't want to use templates if possible and want to use out of the box features.

I guess we'll use merge for now, at least we can keep the service interface on the client nice and tight.

4.0 is noticeably faster for data fetches, especially over WCF. With caching it gives a nice performance hike smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 27-Sep-2013 11:25:25   

NMackay wrote:

They work fine if you specify the types as known types

Well as long as you don't mind seeing a stack trace for your service page then yes, they do simple_smile

It might be some value has to be set on the DataTable to make this work.

That's what I was thinking but I'm not sure which value. I did read about the same restriction elsewhere, I was just hoping you had found a workaround at some stage.

No, sorry.

Out of interest, why are Typedview's based on datatable rather than just been a lightweight collection of entities (basic poco classes since it's read-only)? I've read so many articles saying database over WCF is bad practice although the performance seems fine.

They were typed datatables since day 1, since a lot of controls the objects were used with were actually designed to be used with typed datasets instead of collections of objects (we're talking .net 1.x here). We kept it because of the backwards compatibility. In general it's OK though, fetching a datatable is very fast and all UI controls can deal with it. There's one problem indeed: sending it over the wire is less ideal.

We don't want to use templates if possible and want to use out of the box features.

But using an additional template is just like writing some code: you utilize a feature of our tool by using an extra template to generate classes for you. Why not use that? simple_smile

We have planned to create typedlist/view classes as poco classes though, and generate queryspec or linq queries for typedlists like we generate linq queries for ef typedlists, however it will be a choice for the user: as datatables (which will always be the default) or poco classes which are fetched into an IEnumerable<T>.

Though it won't be in v4.1 though, we'll push this to a later version where we'll focus on services in general more.

4.0 is noticeably faster for data fetches, especially over WCF. With caching it gives a nice performance hike smile

simple_smile Yes fetches are very fast now, in the next upgrade we'll up the speed for inserts/updates/deletes as well a bit (not as big as the update for fetches, but still something simple_smile ).

Frans Bouma | Lead developer LLBLGen Pro