Why WCF custom ServiceHostFactory getting called every time? - wcf

I have a WCF service (hosted in IIS 7.5) which has SVC file less activation using the following
<serviceHostingEnvironment>
<serviceActivations>
<add factory="String"
service="String"/>
</serviceActivations>
</serviceHostingEnvironment>
I am using a custom host factory not the default one.
And the service is WSTrustServiceContract. Now if you see the declaration of this class
[ServiceBehaviorAttribute(Name = "SecurityTokenService", Namespace = "http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice",
InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
[AspNetCompatibilityRequirementsAttribute(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class WSTrustServiceContract : IWSTrustFeb2005SyncContract,
IWSTrust13SyncContract, IWSTrustFeb2005AsyncContract, IWSTrust13AsyncContract, IWsdlExportExtension, IContractBehavior
So we can infer that
The custom host factory will be called to create instance of the service.
Once the service instance is created, it will be there for all the requests (InstanceContextMode.Single)
My questions are
Why the instance of custom host factory is getting created per call (I have a breakpoint in the constructor of this factory. I am debugging by attaching to w3wc instance) ?
Why instance of my service is getting created per call?

As I said, I created simple application and it worked correctly. So try to compare that:
ServiceHost
Public Class MyServiceHost
Inherits ServiceHost
Public Sub New(serviceType As Type, baseAddresses As Uri())
MyBase.New(serviceType, baseAddresses)
End Sub
End Class
Public Class MyServiceHostFactory
Inherits ServiceHostFactory
Public Sub New()
Trace.TraceInformation("MyServiceHost instance created.")
End Sub
Protected Overrides Function CreateServiceHost(serviceType As Type, baseAddresses As Uri()) As ServiceHost
Return New MyServiceHost(serviceType, baseAddresses)
End Function
End Class
Service
<ServiceContract>
Public Interface IService
<OperationContract()>
Function SayHello(name As String) As String
End Interface
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Multiple)>
<AspNetCompatibilityRequirements(RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)>
Public Class Service
Implements IService
Public Sub New()
Trace.TraceInformation("Service instance created.")
End Sub
Public Function SayHello(name As String) As String Implements IService.SayHello
Trace.TraceInformation("Calling Service.SayHello({0}).", name)
Return "Hello " & name
End Function
End Class
Public Class Global_asax
Inherits HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
Trace.TraceInformation("Application started")
End Sub
End Class
Web.config
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
<serviceActivations>
<add factory="FileLessActivationWithCustomServiceHost.MyServiceHostFactory"
service="FileLessActivationWithCustomServiceHost.Service"
relativeAddress="Service.svc"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
<system.diagnostics>
<trace autoflush="true" indentsize="4">
<listeners>
<remove name="Default" />
<add name="Default"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="log.svclog" />
</listeners>
</trace>
</system.diagnostics>
I started web application, made several calls of SayHello function and as you can see bellow, it behaves correctly:

Related

WCF webservice with custom certificate validation

I am hosting a WCF webservice with custom certificate validation, but I am not able to configure it properly. When I try to get the WSDL of the WebService, I get a compilation error below. What am I doing wrong?
Thanks
Edit:
I've looked into: Custom certificate validation in WCF service and authentication of clientCertificate Element and How to: Create a Service that Employs a Custom Certificate Validator and X.509 Certificate Validator and none of those links describe an issue I am having.
Compilation Error message:
Could not load file or assembly 'service' or one of its dependencies. The system cannot find the file specified.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IO.FileNotFoundException: Could not load file or assembly 'service' or one of its dependencies. The system cannot find the file specified.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
web.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="TransportSecurity">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpsGetEnabled="true" httpsGetUrl="" />
<serviceDebug includeExceptionDetailInFaults ="true"/>
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="Custom" customCertificateValidatorType = "MyProject.MyX509CertificateValidator, service"/>
</clientCertificate>
<serviceCertificate findValue="hashvalue" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="clientBehavior">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="Custom" customCertificateValidatorType="MyProject.MyX509CertificateValidator, client"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="MyProject.MyProjectWCF" behaviorConfiguration="MyServiceBehavior">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="TransportSecurity" contract="MyProject.IMyProjectWCF" />
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
WCF code:
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports System.IdentityModel.Selectors
Imports System.Security.Cryptography.X509Certificates
Imports System.IdentityModel.Tokens
Imports System.ServiceModel.Security
Namespace MyProject
' NOTE: You can use the "Rename" command on the context menu to change the class name "MyProjectWCF" in code, svc and config file together.
<ServiceBehavior()> _
Public Class MyProjectWCF
Implements IMyProjectWCF
Public Function HelloWorld() As String Implements IMyProjectWCF.HelloWorld
Return "nameSpace: [" + Me.GetType().Namespace + "]" + vbNewLine + "Normal response"
End Function
Sub New()
Dim serviceHost As New ServiceHost(GetType(MyProjectWCF))
Try
serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom
serviceHost.Credentials.ClientCertificate.Authentication.CustomCertificateValidator = New MyX509CertificateValidator("CN=MyCertificate")
serviceHost.Open()
'serviceHost.Close()
Finally
'serviceHost.Close()
End Try
End Sub
End Class
Public Class MyX509CertificateValidator
Inherits X509CertificateValidator
Private allowedIssuerName As String
Public Sub New(ByVal allowedIssuerName As String)
If allowedIssuerName Is Nothing Then
Throw New ArgumentNullException("allowedIssuerName")
End If
Me.allowedIssuerName = allowedIssuerName
End Sub
Public Overrides Sub Validate(ByVal certificate As X509Certificate2)
' Check that there is a certificate.
If certificate Is Nothing Then
Throw New ArgumentNullException("certificate")
End If
' Check that the certificate issuer matches the configured issuer.
If allowedIssuerName <> certificate.IssuerName.Name Then
Throw New SecurityTokenValidationException _
("Certificate was not issued by a trusted issuer")
End If
End Sub
End Class
End Namespace
Interface code:
Imports System.ServiceModel
Imports System.Security.Permissions
Namespace MyProject
' NOTE: You can use the "Rename" command on the context menu to change the interface name "IMyProjectWCF" in both code and config file together.
<ServiceContract([Namespace]:="MyProject")> _
Public Interface IMyProjectWCF
<OperationContract()> _
Function HelloWorld() As String
End Interface
End Namespace
EDIT 2 (with fix):
Insert default constructor into the cert validator class:
Public Sub New()
Me.New("CN=yourCertificate here")
End Sub
And then I had to figure out what the project name of my website is, which is App_Code, it gets compiled with a bunch of other pages into one DLL, which is APP_Code.dll. The final line in web.config looks like this:
<authentication certificateValidationMode="Custom" customCertificateValidatorType="MyProject.MyX509CertificateValidator, App_Code"/>
So now there are no compiled errors and I get my WSDL. Thank you for your help :)
I think that you have to change this
customCertificateValidatorType =
"MyProject.MyX509CertificateValidator, service"/>
to
customCertificateValidatorType =
"MyProject.MyX509CertificateValidator, MyProject"/>
Because 'service' it's not in your namespace. Maybe you are pasting it from MSDN, but you have to think that the MSDN WCF demo projects ('101 samples'), used to be called 'service'.

Unable to resolve WCF "Metadata publishing for this service is currently disabled" error

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.

Trace WCF call from client

I've built a windows forms client application which makes a WCF call. I'd like to be able to display the actual request and response, includeing SOAP header, and information about the POST/Get, from within the application. Is there a way to configure a trace listener on the client and consume it from within the client, displaying the contents in a text box?
I've configured tracing which outputs messages to a file. This configuration is on my client application, so it's logging all calls it is making to the wcf service and the response. So the sources have been added and for each source is an XmlTraceListener, which handles writing to an xml log file. What I'm looking to do now is utilize a trace listener from within the client application itself and write to a textbox control.
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="All">
<listeners>
<add name="xmlTraceListener" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging"
switchValue="All">
<listeners>
<add name="xmlTraceListener" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xmlTraceListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="ClientLogBasic.svclog" />
</sharedListeners>
<trace autoflush="true" />
</system.diagnostics>
<!-- child of the <system.serviceModel> element -->
<diagnostics>
<messageLogging maxMessagesToLog="10000"
logEntireMessage="true"
logMessagesAtServiceLevel="true"
logMalformedMessages="true"
logMessagesAtTransportLevel="true">
<filters>
<clear/>
</filters>
</messageLogging>
</diagnostics>
So now that I've got message logging working, I create my own trace listener that can write to a textbox:
public class MyTraceListender : System.Diagnostics.TraceListener
{
private TextBox txt_m;
public MyTraceListender(TextBox txt)
: base()
{
txt_m = txt;
}
private delegate void delWrite(string sText);
public override void Write(string message)
{
txt_m.Invoke(new delWrite(AsyncWrite), message);
}
public override void WriteLine(string message)
{
this.Write(message + System.Environment.NewLine);
}
private void AsyncWrite(string sMessage)
{
this.txt_m.AppendText(sMessage);
}
}
Now that I've got my trace listener, I want to try using it from my windows forms client application.
public partial class Form1 : Form
{
private MyTraceListender listener_m;
public Form1()
{
InitializeComponent();
listener_m = new MyTraceListender(this.txtOutput);
System.Diagnostics.Trace.Listeners.Add(listener_m);
}
private void button1_Click(object sender, EventArgs e)
{
ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
string sValue = client.GetData(1234);
}
}
At this point, I'm still not seeing logging to the textbox control. I'm thinking that the listener is not associated to the source, so I tried the following:
public partial class Form1 : Form
{
private MyTraceListender listener_m;
public static System.Diagnostics.TraceSource source = new System.Diagnostics.TraceSource("System.ServiceModel.MessageLogging", System.Diagnostics.SourceLevels.All);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
string sValue = client.GetData(1234);
}
private void Form1_Load(object sender, EventArgs e)
{
listener_m = new MyTraceListender("test", this.txtOutput);
source.Listeners.Add(listener_m);
}
}
After all of this, messages are not being logged to the textbox on the form. They are being logged to the file via the XmlTraceListener, so I'm assuming the issue is how I'm adding my custom listener via System.Diagnostics.Trace.Listeners.Add(). Is this the correct approach?
Edit the Configuration file using WCF Configuration Editor, and enable tracing as shown below
This solution is mostly through app.config. Let me know if you prefer a code-based solution.
In app.config, add your listener to the list of shared listeners, as well as a listener for the message logging source. In 'listeners' list, the name must match the name later on in the 'sharedListeners' list. In 'sharedListeners' list, the 'type' must include full class name with namespace, as well as assembly name:
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="All">
<listeners>
<add name="xmlTraceListener"/>
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging" switchValue="All">
<listeners>
<add name="xmlTraceListener"/>
<add name="textBoxListener"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xmlTraceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="ClientLogBasic.svclog"/>
<add name="textBoxListener" type="WinTransmitterClient.MyTraceListener, WinTransmitterClient" initializeData=""/>
</sharedListeners>
<trace autoflush="true"/>
Because MyTraceListener will be constructed by the framework, its constructor must match the base class. So instead, make the textbox a property that can be set when needed.
In MyTraceListener.cs:
public class MyTraceListener : System.Diagnostics.TraceListener
{
public TextBox txt_m
{
get;set;
}
public MyTraceListener()
: base()
{}
// rest as before ...
In Form1.cs, grab the custom listener after the client is created, and set the text box. No other code is needed. This is the entire Form1.cs, excluding 'using' and 'namespace' lines:
public partial class Form1 : Form
{
public static System.Diagnostics.TraceSource source = new System.Diagnostics.TraceSource("System.ServiceModel.MessageLogging", System.Diagnostics.SourceLevels.All);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ServiceReference1.ContactManagerTextServiceClient client = new ServiceReference1.ContactManagerTextServiceClient();
// identifier in quotes must match name from config file
MyTraceListener mtl = source.Listeners["textBoxListener"] as MyTraceListener;
// of course this doesn't need to be done at every button click, but you get the idea
mtl.txt_m = this.txtOutput;
string sValue = client.GetData(1234);
}
}
Try creating a message inspector. On the client side you'd have to implement IClientMessageInspector which will allow you to intercept the message before it is being sent as well as the response which is received prior to passing it further into the application. You'll find more information on MSDN

using a wcf service that contains a DataContract inside Excel VBA

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.

Class Library Access in WCF

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).