I have two classes in two files in a class library project, they are:
Public Class Logins
Public CurrentUser As Login
Public Function Authenticate(ByVal id As String, ByVal pw As String)
Dim adpt As New WorkMateDataSetTableAdapters.LoginsTableAdapter
For Each k As WorkMateDataSet.LoginsRow In adpt.GetDataByUserName(id)
If String.Equals(k.UserPW, pw) Then
CurrentUser = New Login(k.UserName, k.UserPW, k.UserType)
Return CurrentUser
Exit Function
End If
Next
CurrentUser = Nothing
Return Nothing
End Function
End Class
Public Class Login
Private _UserName As String
Private _UserPW As String
Private _UserType As String
Property UserName
Get
Return _UserName
End Get
Set(value)
_UserName = value
End Set
End Property
Property UserPW
Get
Return _UserPW
End Get
Set(value)
_UserPW = value
End Set
End Property
Property UserType
Get
Return _UserType
End Get
Set(value)
_UserType = value
End Set
End Property
Public Sub New()
UserName = ""
UserPW = ""
UserType = ""
End Sub
Public Sub New(ByVal Namee As String, ByVal pw As String, ByVal typee As String)
UserName = Namee
UserPW = pw
UserType = typee
End Sub
End Class
the other class is:
public Class Courses
Public CoursesOffered As List(Of Course)
End Class
Public Class Course
'Cource name, Exams, Papers
Private _CourseCategory As String
Private _CourseID As String
Public _Sems As New List(Of Sem)
End Class
I used a WCF service using net.tcp connection, however, the issue is that I am able to use the login original classes directly like ( Public Logins As New ServiceLogins.Logins), but I am not able to the course class like that.
Following is the code for the two WCF services:
Imports System.ServiceModel
<ServiceContract()>
Public Interface IService1
<OperationContract()>
Function Test(ByVal value As Integer) As WorkMateLib.Course
<OperationContract()>
Function GetData(ByVal value As Integer) As String
' TODO: Add your service operations here
End Interface
and the other file is:
Imports System.ServiceModel
' NOTE: You can use the "Rename" command on the context menu to change the interface name "ILoginsRelated" in both code and config file together.
<ServiceContract()>
Public Interface ILoginsRelated
<OperationContract()>
Function Authentication(ByVal login As WorkMateLib.Login) As WorkMateLib.Login
End Interface
Your help in this issue is appreciated. Thank you.
Edit:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<services>
<service name="WorkMateWCF.WorkMateWCFService1">
<endpoint address="" binding="netTcpBinding" bindingConfiguration=""
contract="WorkMateWCF.IService1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8523/WorkMateWCFServiceHost" />
</baseAddresses>
</host>
</service>
<service name="WorkMateWCF.LoginsRelated">
<endpoint address="" binding="netTcpBinding" bindingConfiguration=""
contract="WorkMateWCF.ILoginsRelated">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8732/WCFLoginsHost" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
It's not entirely clear what you are trying to achieve but the Course class only exposes one public property named _sem that WCF will serialize as an array of Sem type objects. You should have no problems using the Courses type in the service code but since it is not part of the service contract, it is not available to clients consuming that service contract.
EDIT:
To make Courses available to the clients, you could do something along the lines of this:
'''''''Rest of contract snipped
<OperationContract()>
Function GetCourses() As WorkMateLib.Courses
'''''''Rest of contract snipped
The key is to add any class in the service you want exposed to clients part of the service contract by referencing it as either an input parameter or an output value of a service operation (function).
Related
I am trying to make my Windows Service, which hosts a WCF service cross-domain compatible.
After hours and hours of searching I was told that I needed to create another service which will load the crossdomain.xml and clientaccesspolicy.xml files so that the Windows hosted WCF service can be run on any domain.
This is my main service :
Namespace UmbrellaMobileService
<RunInstaller(True)> _
Public Class ProjectInstaller
Inherits Installer
Private process As ServiceProcessInstaller
Private service As ServiceInstaller
Private components As System.ComponentModel.Container
Public Sub New()
process = New ServiceProcessInstaller()
process.Account = ServiceAccount.LocalSystem
service = New ServiceInstaller()
service.ServiceName = "UmbrellaMobileService"
service.DisplayName = "Umbrella Mobile Service"
service.Description = "Handels Umbrella Mobile Requests."
Installers.Add(process)
Installers.Add(service)
End Sub
End Class
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)> _
Public Class UmbrellaMobileService
Inherits ServiceBase
Public serviceHost As ServiceHost = Nothing
Public CrossDomainServiceHost As ServiceHost = Nothing
Public Shared Sub Main()
ServiceBase.Run(New UmbrellaMobileService())
End Sub
Public Sub New()
ServiceName = "UmbrellaMobileService"
End Sub
'Start the Windows service.
Protected Overloads Overrides Sub OnStart(ByVal args As String())
If serviceHost IsNot Nothing Then
serviceHost.Close()
End If
serviceHost = New WebServiceHost(GetType(UmbrellaService), New Uri("http://localhost/UmbrellaMobileService"))
serviceHost.AddServiceEndpoint(GetType(IUmbrellaMobileService), New WebHttpBinding(), "http://localhost/UmbrellaMobileService")
CrossDomainServiceHost = New ServiceHost(GetType(CrossDomainService))
Else
System.Diagnostics.EventLog.WriteEntry("Umbrella Mobile Service", objIniFile.ErrorMessage, EventLogEntryType.Error)
serviceHost.Close()
CrossDomainServiceHost.Close()
End If
serviceHost.Open()
CrossDomainServiceHost.Open()
End Sub
' Stop the Windows service.
Protected Overloads Overrides Sub OnStop()
If serviceHost IsNot Nothing Then
serviceHost.Close()
CrossDomainServiceHost.Close()
serviceHost = Nothing
End If
End Sub
End Class
<AspNetCompatibilityRequirements(Requirementsmode:=AspNetCompatibilityRequirementsMode.Allowed)> _
Public Class UmbrellaService
Inherits System.Web.Services.WebService
Implements IUmbrellaMobileService
Function GetCustomers() As Stream Implements IUmbrellaMobileService.GetCustomers
'Function Logic
End Function
End Class
End Namespace
This is my Implementation of my main service:
Namespace UmbrellaMobileService
<ServiceContract()> _
Public Interface IUmbrellaMobileService
<OperationContract()> _
<WebInvoke(Method:="GET", BodyStyle:=WebMessageBodyStyle.Bare, ResponseFormat:=WebMessageFormat.Json, RequestFormat:=WebMessageFormat.Json)> _
Function GetCustomers() As Stream
End Interface
End Namespace
This is the 'Cross Domain Service' I was advised to add:
Namespace UmbrellaMobileService
Public Class CrossDomainService
Implements ICrossDomainService
Public Function ProvidePolicyFile() As System.ServiceModel.Channels.Message Implements ICrossDomainService.ProvidePolicyFile
Dim filestream As FileStream = File.Open("ClientAccessPolicy.xml", FileMode.Open)
' Either specify ClientAccessPolicy.xml file path properly
' or put that in \Bin folder of the console application
Dim reader As XmlReader = XmlReader.Create(filestream)
Dim result As System.ServiceModel.Channels.Message = Message.CreateMessage(MessageVersion.None, "", reader)
Return result
End Function
End Class
End Namespace
And here is its Implementation :
Namespace UmbrellaMobileService
<ServiceContract()> _
Public Interface ICrossDomainService
<OperationContract(), WebGet(UriTemplate:="ClientAccessPolicy.xml")> _
Function ProvidePolicyFile() As Message
End Interface
End Namespace
My config file looks like this :
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="CrossDomainServiceBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="UmbrellaMobileService.UmbrellaMobileService">
<endpoint address="" binding="basicHttpBinding" contract="UmbrellaMobileService.IUmbrellaMobileService">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8731/UmbrellaMobileService"/>
</baseAddresses>
</host>
</service>
<service name="UmbrellaMobileService.CrossDomainService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8731/"/>
</baseAddresses>
</host>
<endpoint address="" binding="webHttpBinding" contract="UmbrellaMobileService.ICrossDomainService" behaviorConfiguration="CrossDomainServiceBehavior"/>
</service>
</services>
</system.serviceModel>
<system.web>
<compilation debug="true"/></system.web></configuration>
Now, I get this error when I attempt to run this service:
Service cannot be started. System.InvalidOperationException: Service has zero application (non-infrastructure) endpoints.
This might be because no configuration file was found for your application,
or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.
I have searched and searched and still do not know what to do, can anyone give me some advice?
I had the same issue and here is how I fixed it.
First, create a new service class that will serve up the clientaccesspolicy.xml when a client requests it:
[ServiceContract]
public class CrossDomainPolicyService
{
private Stream StringToStream(string result)
{
WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
return new MemoryStream(Encoding.UTF8.GetBytes(result));
}
[OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
public Stream GetClientAccessPolicy()
{
string result = #"<?xml version=""1.0"" encoding=""utf-8""?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers=""*"">
<domain uri=""*""/>
</allow-from>
<grant-to>
<resource path=""/"" include-subpaths=""true""/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>";
return StringToStream(result);
}
}
Next, modify your app.config file to expose the new service:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="policyBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<services>
<service name="MyNamespace.MyService">
<endpoint address="" binding="basicHttpBinding" contract="MyNamespace.IMyService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9876/MyService/" />
</baseAddresses>
</host>
</service>
<service name="MyNamespace.CrossDomainPolicyService">
<endpoint address="" binding="webHttpBinding" contract="MyNamespace.CrossDomainPolicyService" behaviorConfiguration="policyBehavior">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<host>
<baseAddresses>
<!-- Root Domain where the other service is hosted -->
<add baseAddress="http://localhost:9876/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
... and that should do it!
I'm branching out into .Net web services and unfortunately am a newbie at it. I've decided to build WCF service instead of asp.net service because of online recommendations. My ultimate goal is to learn iOS and other mobile programming. I'm familiar with vb.net and c# standard and web applications.
I'm receiving a "Metadata publishing for this service is currently disabled" error when trying to test from a URL. I've research and tried implementing "fixes" for this issue, but still come up short. Can someone please look at my code and see what I'm doing wrong?
Webconfig file
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" strict="false"
explicit="true" targetFramework="4.0" />
<customErrors mode="Off"/>
</system.web>
<connectionStrings>
</connectionStrings>
<system.serviceModel>
<services>
<services>
<service name="CCT_Main_SRV.Service1" behaviorConfiguration="BehConfig">
<endpoint address="" binding="webHttpBinding" contract="CCT_Main_SRV.Service1" behaviorConfiguration="web">
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BehConfig">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
</serviceHostingEnvironment>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
.VB File
Imports System.ServiceModel
Imports System
Namespace CCT_Main_SRV.Service1
<ServiceContract()>
Public Interface CCT_Main_SRV
<OperationContract()>
<WebGet(UriTemplate:="Get_Device_Authenication", _
RequestFormat:=WebMessageFormat.Json, _
ResponseFormat:=WebMessageFormat.Json, _
BodyStyle:=WebMessageBodyStyle.Wrapped)>
Function Authenicate_Device_Manager(ByVal Device_Name As String, _
ByVal Auth_Key As String) _
As List(Of Device_Authenication)
<WebGet()>
<OperationContract()>
Function Authenicate_Device_Manager_Non_JSON(ByVal Device_Name As String, _
ByVal Auth_Key As String) _
As List(Of Device_Authenication)
End Interface
<DataContract()>
Public Class Device_Authenication
<DataMember()>
Public Property Device_Name As String
<DataMember()>
Public Property Active As Boolean
<DataMember()>
Public Property Return_String As String
End Class
svc.vb file
Imports System.ServiceModel
Imports System.Web.Script.Serialization
Imports System.ServiceModel.Activation
Imports System.ServiceModel.Web
Imports System.ServiceModel.Description
Namespace CCT_Main_SRV.Service1
Public Class Service1
Implements CCT_Main_SRV
Dim host As WebServiceHost = New WebServiceHost(GetType(Service1), New Uri("http://pmh-vmutility-1/cct_web_srv_test/:8000/"))
Dim ep As ServiceEndpoint = host.AddServiceEndpoint(GetType(CCT_Main_SRV), New WebHttpBinding(), "")
Public Function Authenicate_Device_Manager(ByVal Device_Name As String, _
ByVal Auth_Key As String) _
As List(Of Device_Authenication) _
Implements CCT_Main_SRV.Authenicate_Device_Manager
End Function
End Class
End Namespace
service name="CCT_Main_SRV.Service1"
Should be:
service name="Service1"
Or if it resides in a Web Application:
service name="ProjectName.Web.Service1"
It should match the fully qualified name of your service
{namespace}.{class} yours does not have a namespace.
I'm getting the notorious, "The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error" in my project. It's a WCF PollingDuplex Service, consumed by a Silverlight 4 project.
I'm requesting a document with the service, so I can display it in a viewer in my SL application.
Here is the Server Web Config XML:
<system.serviceModel>
<extensions>
<bindingExtensions>
<add name="pollingDuplex" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement,System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</bindingExtensions>
</extensions>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<behaviors>
<serviceBehaviors>
<behavior name="PortalOnlineBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<pollingDuplex>
<binding name="SLPollingDuplex" duplexMode="MultipleMessagesPerPoll" />
</pollingDuplex>
</bindings>
<services>
<service name="Online.Web.PortalOnline" behaviorConfiguration="PortalOnlineBehavior">
<endpoint address="" binding="pollingDuplex" bindingConfiguration="SLPollingDuplex"
contract="Notification.IPortalOnline" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://portalonline.com/PortalOnline/IPortalOnline" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
Here is the Object I'm trying to return to SL via this WCF service
Imports System.ServiceModel
Imports System.Runtime.Serialization
Imports DocCave.Common
Imports System.Xml
Imports System.IO
<DataContract(NAMESPACE:=DataStore.NAMESPACE)>
Public Class PortalDocument
<DataMember()>
Public Property DataSource As Byte()
<DataMember()>
Public Property FileName As String
<DataMember()>
Public Property FileType As String
End Class
Here's the WCF Method that is being called:
Public Function GetDocument(sessionUserMeta As Common.UserMetaData, docId As System.Guid) As Notification.PortalDocument Implements Notification.IPortalOnline.GetDocument
Dim doc As Documents.Document = Documents.Document.GetDocument(docId, sessionUserMeta)
Dim portalDoc As New PortalDocument
portalDoc.DataSource = doc.DataSource
portalDoc.FileName = doc.QueryPackage.DocumentName
portalDoc.FileType = doc.QueryPackage.Type
Return portalDoc
End Function
Further Details:
This works perfectly for one or two document request, and the gives me the above mentioned error. For instance, I can load a default document when the SL application is loaded using this method with this service, and it populates perfectly. I can then go to a tree view I have, and select a document, and it works perfect for the first document... but after that, error. Also, I've noticed sometimes it will only work once, if I select certain pdfs that are a bit larger (250kb or so..) ... oh, and I forgot... here's the code in my SL application that is connecting to the WCF service. I'm using the "GetBaseWebAddress()" because I'm using dynamic sub domains, so part of the address can be different each time...
Private Sub LoadClient()
Dim bind As New PollingDuplexHttpBinding(PollingDuplexMode.MultipleMessagesPerPoll)
Dim endpoint As New EndpointAddress(GetBaseWebAddress() & "PortalOnline/PortalOnline.svc")
Me.client = New PortalOnline.PortalOnlineClient(bind, endpoint)
AddHandlers()
End Sub
I've struggled with this for a while, so any help would be greatly appreciated...
You read the title and groaned. That's okay. I did too. But we do what we're asked, right? I need to build a service that can be accessed via a moniker from within Excel (2003, but I'm assuming any version of Excel should support this functionality). At the moment all I want to do is have a spreadsheet post data to a WCF service running from a Windows service on a remote machine. Because that data needs to be retrieved by something a little more sophisticated than VBA, I decided to set up a data contract. Here's my code (at the moment this is just a proof-of-concept, but it's closely related to how it needs to look when it's finished).
Here's the WCF-related stuff:
Imports System.ServiceModel
Imports System.Runtime.Serialization
<ServiceContract()>
Public Interface IWCF
<OperationContract()>
Sub PutData(ByVal what As String)
<OperationContract()>
Function GetWhats() As TheWhats()
End Interface
<DataContract()>
Public Class TheWhats
<DataMember()> Public Property Timestamp As DateTime
<DataMember()> Public Property TheWhat As String
End Class
Public Class WCF
Implements IWCF
Shared Whats As New List(Of TheWhats)
Public Sub PutData(ByVal what As String) Implements IWCF.PutData
Whats.Add(New TheWhats With {.Timestamp = Now, .TheWhat = what})
End Sub
Public Function GetWhats() As TheWhats() Implements IWCF.GetWhats
Return Whats.ToArray
End Function
End Class
My app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true"></compilation>
</system.web>
<system.serviceModel>
<services>
<service name="DataCollectionService.WCF">
<endpoint address=""
binding="netTcpBinding"
contract="DataCollectionService.IWCF" />
<endpoint address="mex"
binding="mexTcpBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:9100/DataCollectionService/ "/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
And my vba class to handle posting stuff:
Private Const pConxString As String = _
"service:mexAddress=""net.tcp://localhost:7891/Test/WcfService1/Service1/mex"", " & _
"address=""net.tcp://localhost:7891/Test/WcfService1/Service1/"", " & _
"binding=""NetTcpBinding_IService1"", bindingNamespace = ""http://tempuri.org/"", " & _
"contract=""IService1"", contractNamespace=""http://tempuri.org/"""
Public ServiceObject As Object
Private Sub Class_Initialize()
Set ServiceObject = GetObject(pConxString)
End Sub
Public Sub PutData(ByVal what As String)
ServiceObject.PutData what
End Sub
Private Sub Class_Terminate()
Set ServiceObject = Nothing
End Sub
If I include the DataContract attribute and the function that returns the data contract object, my vba code fails in the Public Sub PutData method with the following:
"Instance of MessagePartDescription Name='GetWhatsResult' Namespace='http://tempuri.org/' cannot be used in this context: required 'Type' property was not set."
If I take out the DataContract and comment out the function in the service definition, I'm fine. I don't plan on using the GetWhats() function from within Excel. But yet I'm guessing it wants the type definition for TheWhats.
From what I've read one solution seems to be making this a COM object and referencing the DLL. However that isn't a workable solution for my environment. Is there another way to fix this?
Okay, answered my own question. The solution (at least in my case) is to split the interface and have my service class implement both interfaces. Here's my new interface file:
Imports System.ServiceModel
Imports System.Runtime.Serialization
<ServiceContract()>
Public Interface IWCF_WriteOnly
<OperationContract()>
Sub PutData(ByVal what As String)
End Interface
<ServiceContract()>
Public Interface IWCF_ReadOnly
<OperationContract()>
Function GetData() As TheWhats()
End Interface
<DataContract()>
Public Class TheWhats
<DataMember()> Public Property Timestamp As DateTime
<DataMember()> Public Property TheWhat As String
End Class
Public Class WCF
Implements IWCF_WriteOnly
Implements IWCF_ReadOnly
Shared Whats As New List(Of TheWhats)
Public Sub PutData(ByVal what As String) Implements IWCF_WriteOnly.PutData
Whats.Add(New TheWhats With {.Timestamp = Now, .TheWhat = what})
End Sub
Public Function GetData() As TheWhats() Implements IWCF_ReadOnly.GetData
Return Whats.ToArray
End Function
End Class
That required a change in app.config so that two separate endpoints could operate on the same address:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true"></compilation>
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="GenericBehavior" name="DataCollectionService.WCF">
<endpoint address="wo" binding="netTcpBinding" contract="DataCollectionService.IWCF_WriteOnly" />
<endpoint address="ro" binding="netTcpBinding" contract="DataCollectionService.IWCF_ReadOnly" />
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:9100/DataCollectionService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="GenericBehavior">
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
And because the write-only endpoint was isolated from the endpoint that required a data contract definition, this change in the Excel definition was necessary:
Private Const pConxString As String = _
"service:mexAddress=""net.tcp://localhost:9100/DataCollectionService/Mex"", " & _
"address=""net.tcp://localhost:9100/DataCollectionService/wo"", " & _
"binding=""NetTcpBinding_IWCF_WriteOnly"", bindingNamespace = ""http://tempuri.org/"", " & _
"contract=""IWCF_WriteOnly"", contractNamespace=""http://tempuri.org/"""
I tested this configuration with the WCF Test Client. I had to feed it the mex endpoint manually, but when I did, it picked up both contracts. I used the PutData method to populate the service class a little, then went into Excel and populated it some more. I went back to the WCF Test Client and ran the GetData function and it returned all the items added from both the Test Client and Excel.
We have not encountered this specific scenario, but we do a lot of work with WCF where we control both the service and consumer (silverlight, tablet os, ipad apps in mono, etc).
The general solution that we use is to have the same classes in the same namespace on both ends of the pipe. I am not 100% sure that this will work in your environment, but it might be worthwhile to recreate your TheWhats class in VBA and see if that helps you.
If it does, and VBA supports it, you can move the class to its own file and the reference it both from the service and client side projects.
My first question so be gentle =)
The following is all .Net 4, VB.Net, in VS2010. The end goal is to receive a (complete) stream from the client over Tcp binding, to an IIS Hosted WCF Service. The problem I am facing is that the service is not able to read any bytes from the provided stream. Now with the nitty gritty... I've removed a fair amount for brevity but, let me know if I've omitted something important.
The service contract is as follows:
<ServiceContract(Namespace:="ImageSystem")> _
Public Interface IUploadService
<OperationContract()> _
Function UploadFile(ByVal file As ImageUpload) As ImageUpload
End Interface
The data contract ImageUpload is as follows:
<MessageContract()> _
Public Class ImageUpload
#Region " Message Header "
Private _ImageID As Nullable(Of Long)
<MessageHeader()> _
Public Property ImageID() As Nullable(Of Long)
Get
Return _ImageID
End Get
Set(ByVal value As Nullable(Of Long))
_ImageID = value
End Set
End Property
'... a few other value type properties
#End Region
#Region " Message Body"
' Do not add any more members to the message body or streaming support will be disabled!
<MessageBodyMember()> _
Public Data As System.IO.Stream
#End Region
End Class
The relevant server config/bindings are as follows (these are obviously dev environment settings only):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpStreamBinding" transferMode="Streamed" maxBufferSize="20971520" maxReceivedMessageSize="20971520"/>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="UploadServiceBehaviour"
name="ImageSystem.SVC.UploadService">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpStreamBinding"
contract="ImageSystem.SVC.IUploadService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:809/UploadService" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="UploadServiceBehaviour">
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
The WCF Service is a service Library, which is hosted by a Web Application. The Web Application project runs in my local IIS 7.5. IIS has been configured to enable TCP connections, and the application pool identity is configured with relevant permissions to the contract implementation. VS2010 is run as admin to enable debugging in IIS.
To test the contract implementation I have a Windows Console Application set up as a (test) client. The client proxy classes were generated by adding a service reference to the service within the IIS host (http://localhost/ImageSystem/UploadService.svc). The service reference is configured to generate async methods.
The relevant auto-generated client config is as follows (note, I've tried increasing maxBufferPoolSize, maxBufferSize, and maxReceivedMessageSize to match the servers config of "20971520", but to no avail):
[EDIT: reliableSessions section commented out in light of Sixto Saez's suggestion but to no avail]
<system.serviceModel>
<bindings>
<binding name="NetTcpBinding_IUploadService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Streamed" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="20971520"
maxBufferSize="20971520" maxConnections="10" maxReceivedMessageSize="20971520">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<!--<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />-->
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://mycomputername.mydomain/ImageSystem/UploadService.svc"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IUploadService"
contract="UploadService.Local.IUploadService" name="NetTcpBinding_IUploadService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
The client usage is as follows:
Public Sub Test()
Dim serviceClient As UploadService.Local.UploadServiceClient = New UploadService.Local.UploadServiceClient
AddHandler serviceClient.UploadFileCompleted, AddressOf LocalTestCallback
Dim ms As MemoryStream = New MemoryStream
My.Resources.Penguins.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "MYDOMAIN"
serviceClient.ClientCredentials.Windows.ClientCredential.UserName = "User"
serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "Password123"
serviceClient.UploadFileAsync(Nothing, ..., ms, ms) '"..." is obviously not actually here, other values omitted. "ms" is passed as UserState object in addition to fulfilling the 'Data' parameter
End Sub
In case you wonder (or it matters), the penguins image is the one provided with Windows 7 in the sample pictures directory. The image is 777,835 bytes (should be within the relevant request/buffer max sizes).
I have tried two approaches to read the image on the server side.
Approach 1:
Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile
Dim uploadBuffer(Helper.Settings.AppSettings(Of Integer)("UploadBufferSize", True) - 1) As Byte
Dim ms As MemoryStream = New MemoryStream()
Dim bytesRead As Integer
Do
bytesRead = file.Data.Read(uploadBuffer, 0, uploadBuffer.Length)
ms.Write(uploadBuffer, 0, bytesRead)
Loop Until bytesRead = 0
End Function
Approach 2:
Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile
Dim reader As StreamReader = New StreamReader(file.Data)
Dim imageB64 As String = reader.ReadToEnd
ms = New MemoryStream(Convert.FromBase64String(imageB64))
End Function
In both cases, ms.Length = 0. More clearly, in the second approach, imageB64 = "" (empty string).
Why aren't I receiving anything from the stream? Also, as a sneaky sub-question, why does the generated proxy class not provide an overload that accepts an object of type ImageUpload?
Thank you in advance!!
it seemed strange to me that you would have the problems you mentioned, so I was curious and put together an implementation using your service contract. That one actually worked straight away. I don't actually know what went wrong in your case (it's not obvious), but let me just post a working solution here hoping that this will help you to solve your problem.
Unfortunately, as I abandoned VB many years ago, I can only provide C# code. Hope that's alright.
Server Web.config (tested in IIS, with net.tcp binding):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding transferMode="Streamed" maxReceivedMessageSize="1000000">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="ImageSystem.SVC.UploadService">
<endpoint address="" binding="netTcpBinding" contract="ImageSystem.SVC.IUploadService">
</endpoint>
<endpoint address="mex" kind="mexEndpoint" binding="mexTcpBinding"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Client app.config (console test app):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding transferMode="Streamed" maxReceivedMessageSize="1000000">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint
address="net.tcp://localhost/WcfService1/UploadService.svc"
binding="netTcpBinding"
contract="ImageServices.IUploadService"
name="NetTcpBinding_IUploadService">
</endpoint>
</client>
</system.serviceModel>
Service contract & implementation:
[ServiceContract(Namespace="urn:ImageSystem")]
public interface IUploadService
{
[OperationContract]
ImageUpload UploadFile(ImageUpload file);
}
[MessageContract]
public class ImageUpload
{
[MessageHeader]
public long? ImageID { get; set; }
[MessageBodyMember]
public Stream Data;
}
public class UploadService : IUploadService
{
public ImageUpload UploadFile(ImageUpload file)
{
long length;
using (var ms = new MemoryStream())
{
file.Data.CopyTo(ms);
length = ms.Length;
}
return new ImageUpload { ImageID = length, Data = new MemoryStream() };
}
}
Test app:
private static readonly string imgPath = #"C:\Pictures\somepicture.jpg";
private static readonly EventWaitHandle waitHandle = new AutoResetEvent(false);
static void Main()
{
long? result;
using (var service = new ImageServices.UploadServiceClient("NetTcpBinding_IUploadService"))
{
var image = new ImageServices.ImageUpload();
using (var imgStream = File.OpenRead(imgPath))
{
image.Data = imgStream;
service.UploadFileCompleted += (sender, e) =>
{
result = e.Result;
if (e.Data != null) image.Data.Dispose();
waitHandle.Set();
};
service.UploadFileAsync(null, imgStream);
waitHandle.WaitOne();
}
}
}
First of all, as you can see, the config files can be a lot simpler. Especially the large BufferSize value is not necessary. Then, with regard to the service contract, it's not clear to me why the Upload operation would receive AND return an ImageUpload message. In my implementation, I'm returning the uploaded file size in the ImageID parameter just for demo purposes, of course. I don't know what your reasoning behind that contract was, and what you actually would want to return.
Actually, was just about to click "Send" when I had an idea why your code could have failed. In your test client, before you call serviceClient.UploadFileAsync(), add this line to your code: ms.Seek(0, SeekOrigin.Begin).
This resets the position of the MemoryStream back to its beginning. If you don't do that, the MemoryStream will be consumed only from its current position, which is its end - and which explains the Length = 0 of the stream received on the service side!
You may have already seen the information in this MSDN article but you should review the section called Streaming Data and the restrictions it lists. Your client config shows the reliableSession element having the ordered attribute set to "true" which is not supported for streaming.
I don't know if that is specifically the cause of your issue but it is a start. That article also layouts pretty well the basic config required for streaming so you should ensure your config complies with its recommendations.