OK so in the end in order to fully honour all my requirements I have basically had to ditch the idea of using the automated tools and hand craft each client myself.
This isn't as hard as it sounds though.
I basically created an abstract generic base class ServiceClient where T is a ServiceContract interface (which I had already defined)
The job of the ServiceClient it similar to the ones generated by the VS and CLI tools (svcutil, Etc). It exposed a "Channel" to the derived classes through a ServiceChannel Property. This is achieved using:
// T here is the ServiceContract as configured by the derived class.
ChannelFactory<T>.CreateChannel(binding, endpoint);
I now have the ability to create clients as follows:
class PeopleServiceClient : ServiceClient<IPeopleService>, IPeopleService {
public Person[] Find(Person person) {
// Delegate all responsability to the channel (which is connected to the API)
return base.ServiceChannel.Find(person);
}
}
Because everything is associated with the IPeopleService interface I can guarantee that each method have been implemented, or at least changes will be noticed at compile time.
I can now use the client like this:
using(var client = new PeopleServiceClient()) {
Person[] people = client.Find(new Person() { Name = "Gary" });
}
So to answer my question:
Yes, you can have an all encompassing library containing models, contracts and clients; You just have to code it all yourself
So if you don't mind coding a few more classes and not rely on the VS generated code then I think this approach is acceptable.
In case you find it helpful, below a first draft of the ServiceClient class (there WILL be bugs and unforeseen issues so please use for learning purposes only):
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/// <summary>
/// Abstract base class for Service Clients wishing to utilise an API
/// via some service contract channel.
/// </summary>
public abstract class ServiceClient<T> : IDisposable where T : class {
/// <summary>
/// Creates and configures the ServiceClient and opens the connection
/// to the API via its Channel.
/// </summary>
protected ServiceClient(EndpointAddress endpoint, Binding binding) {
this.ServiceChannel = ChannelFactory<T>.CreateChannel(binding, endpoint);
(this.ServiceChannel as ICommunicationObject).Open();
}
/// <summary>
/// Closes the client connection.
/// </summary>
public void Close() {
(this.ServiceChannel as ICommunicationObject).Close();
}
/// <summary>
/// Releases held resources.
/// </summary>
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Closes the client connection and releases any additional resources.
/// </summary>
protected virtual void Dispose(bool disposing) {
if (disposed) return;
if (disposing) {
if (this.ServiceChannel != null) {
this.Close();
this.ServiceChannel = null;
this.disposed = true;
}
}
}
/// <summary>
/// Provides a derived class access to the API via a dedicated channel.
/// </summary>
protected T ServiceChannel { get; private set; }
/// <summary>
/// Indicates if this instance has been disposed.
/// </summary>
private bool disposed = false;
}