In my service code I can define an interface for a service (IMyService),
and I can thereafter define several service implementations of this
interface (ServiceImpl_1, ServiceImpl_2 etc).
In my client can I know the interface IMyService? It appears that the
normal way to reference the concrete services is to make a "service
reference" to either ServiceImpl_1 or ServiceImpl_2, and use the generated
proxies to call the concrete services.
What I would like is at my client to have a factory which knows the
concrete service implementations, but returns a "IMyService" to my client
program. So my client program only knows the service interface, not
anything about the concrete implementations.
Is this at all possible?
Thanks,
Peter
To call a service you need a proxy. You can only have a proxy by using WSDL.
The WSDL is either available via GET or via a specific endpoint
IMetadataExchange. Afterwords, no need for WSDL any more and its up to you
to abstract the use of the proxy.
One option is to build a helper class that provides the interface (in WS
terms, the portType) the client wants to use. Some sample code:
ChannelFactory<IMyContractChannel> factory =
new ChannelFactory<IMyContractChannel>("myEndpointName");
IMyContractChannel channel = factory.CreateChannel();
try { channel.Add(1, 2); }
finally
{
if( null != channel )
{ try { channel.Close(); } catch(Exception) { channel.Abort(); } }
}
Create a helper class using the code above, you can provide the code with
the appropriate contract interface where the channel.Add call would be made
by the code, not your helper class.
Is this what you intended?
Tiago Halm
Yes, thanks - that was the sort of thing I was looking for. I actually have
found several examples of this sort of thing on the net, after I began to
understand a little more of the technology to know what it actually was I
was asking for.
For example, I have found a GenericProxy class by Guy Burstein. He extends
ClientBase though - not ChannelFactory (why?) - and as a matter of fact I
couldn't compile his code as he uses something called InnerProxy.
One thing: is it necessary to close the channel as you do?
Thanks,
Peter
Extending ClientBase is not the best option. Better to create a class
then deals with the channel creation using the sample code I've given.
You can see that ClientBase, if you use Reflector, uses the
ChannelFactory behind the curtains. ChannelFactory gives you an add-
on, which is a pool of connections. Everytime you close the channel,
you're in fact giving the channel back to the pool of the factory, and
that is the reason why your new helper class must keep the factory
alive. Try to build a helper class. If you need one I'll give you a
sample that does exactly that ... its basically a generic class that
receives as type the interface you're expecting. Something similar to
what someone posted in a forum, which I reused to some extent.
See here:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=915745&SiteID=1
As for the Close method, you do need to call it so the channel goes
back to the pool after being used. What does close do? Well, Close is
in fact a soap message back to the service, and that is why you see my
Close wrapped in a try/catch. This is needed because if you have a
fault while you call Add, and then you call Close, you end up losing
the original exception that caused the error. You should not use
"using", use try/catch/finally.
More info here:
http://blogs.msdn.com/salvapatuel/archive/2007/04/25/why-using-is-bad-for-your-wcf-service-host.aspx
Hope it helps,
Tiago Halm
<snip>
> See here:
> http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=915745&SiteID=1
<snip>
Hi - thanks again for your comments. I looked at the example, but is there
not a bug in this code? I can call
MyServiceChannel channel = ChannelManager<MyServiceChannel>.GetChannel
("MyService");
to get the channel configured as MyService. If I then try
MyServiceChannel channel = ChannelManager<MyServiceChannel>.GetChannel
("MyOtherService");
don't I still get the first channel again as the ChannelFactory which gets
the channel is a static variable (set only the first time).
/Peter
The channel factory is static, because its the instance responsible
for managing its own pool of channel instances. When you get a channel
by calling the GetChannel(endpoint name) where "endpoint name" is the
name of the endpoint as configured in the configuration file. (see
client/endpoint/@name) you asking the factory to create a new channel.
The factory will look into its own pool of channels and see if there
is one available and not in use. If there is it will give you that, if
not, it will create a new one in the pool, set it as "in use" and give
it to you.
The interface you set in the generic argument is the proxy generated
interface (a.k.a. portType) of the service you want to use. Every
service has at least one portType with at least one operation.
If you look into ChannelFactory documentation in MSDN you'll see
details of its inner workings. Reflector can also help there.
Tiago Halm
http://sholliday.spaces.live.com/blog/cns!A68482B9628A842A!158.entry
"Peter K" <xdz...@hotmail.com> wrote in message
news:%23IEnxWo...@TK2MSFTNGP03.phx.gbl...
Thanks ever so much for all your help! I must admit I don't quite
understand your above comments yet.
As far as I can see, because the very first call to
ChannelManager.GetChannel(endpoint) instantiates the static ChannelFactory
with the specified endpoint, then in subsequent calls to GetChannel it is
irrelevant if I pass a new "endpoint" (as the ChannelFactory is already
instantiated based on the first endpoint) and I always get a channel to the
first endpoint.
But I'll investigate further (it's likely I've misunderstood or overlooked
something in the code), and thanks again,
Peter
Peter,
You're quite right, hence the reason why my adaptation has a static
Dictionary<> of factories instances indexed by the endpoint name.
Should be straightforward to change the code to behave like that.
Tiago Halm