More WCF woes... :)
All my workflows implement the same 3 methods. After a lot of copy and paste, I decided to make them inherit from the same interface:
[ServiceContract(Namespace = "http://schema.company.com/messages/")]
public interface IBasicContract<TRequest, TResponse>
where TRequest : class
where TResponse : class
{
[OperationContract(Name = "GetReport",
Action = "http://schema.company.com/messages/GetReport",
ReplyAction = "http://schema.company.com/messages/GetReportResponse")]
TResponse GetReport(TRequest inquiry);
[OperationContract(Name = "GetRawReport",
Action = "http://schema.company.com/messages/GetRawReport",
ReplyAction = "http://schema.company.com/messages/GetRawReportResponse")]
string GetRawReport(string guid);
[OperationContract(Name = "GetArchiveReport",
Action = "http://schema.company.com/messages/GetArchiveReport",
ReplyAction = "http://schema.company.com/messages/GetArchiveReportResponse")]
TResponse GetArchiveReport(string guid);
}
I have also decided to create a common implementation of the service client:
public class BasicSvcClient<TRequest, TResponse> : ClientBase<IBasicContract<TRequest, TResponse>>, IBasicContract<TRequest, TResponse>
where TRequest : class
where TResponse : class
{
public BasicSvcClient()
{
}
public BasicSvcClient(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public BasicSvcClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public BasicSvcClient(string endpointConfigurationName, EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public BasicSvcClient(Binding binding, EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
public TResponse GetReport(TRequest inquiry)
{
return Channel.GetReport(inquiry);
}
public string GetRawReport(string guid)
{
return Channel.GetRawReport(guid);
}
public TResponse GetArchiveReport(string guid)
{
return Channel.GetArchiveReport(guid);
}
}
The problem is when I try to use this:
using (var client = new BasicSvcClient<TRequest, TResponse>())
{
var response = client.GetReport(inquiry);
context.Response.ContentType = "text/xml";
context.Response.Write(response.AsXML());
}
I am always getting an error saying that it cannot find the configuration for contract IBasicContract, in that weird syntax that .NET uses:
Could not find default endpoint element that references contract 'BasicWorkflow.IBasicContract`2...
I tried doing this:
using (var client = new BasicSvcClient<TRequest, TResponse>("myConfig"))
It doesn't help - it's still also looking for that specific contract.
I know that the ServiceContract attribute has a ConfigurationName parameter, but I cannot use that at compile time, because I have many workflows I'm calling from the same program (and therefore many configuration entries). Is there a way to set the ConfigurationName at runtime? I thought that this is what the ClientBase constructor was supposed to do, but apparently not.
[Edit] This is the endpoint in the .config file, I don't believe it's very helpful in this case:
<endpoint address="https://localhost/services/Contract.svc"
binding="basicHttpBinding"
bindingConfiguration="httpsDataEndpoint"
contract="IContract" name="IContractSvc" />
[Edit2] Ok... I found a way that's working, though I'm still not completely satisfied with it:
using (var wf = new BasicSvcClient<TRequest, TResponse>(
new BasicHttpBinding("httpsDataEndpoint"),
new EndpointAddress("https://localhost/services/Contract.svc")))
The only problem I have now is that I would prefer to retrieve the endpoint address from the .config file (using the actual contract name, like IContract). Anybody who can help me with that part?
[Edit3] Finally found the complete solution :) Long live Reflector!
var cf = (ClientSection) ConfigurationManager.GetSection("system.serviceModel/client");
foreach (ChannelEndpointElement endpoint in cf.Endpoints)
{
if (endpoint.Name != "ContractSvc")
continue;
using (var wf = new BasicSvcClient<TRequest, TResponse>(
new BasicHttpBinding("httpsDataEndpoint"),
new EndpointAddress(endpoint.Address.ToString())))
{
//... call wf.GetReport()
}
break;
}